0) {printf("Recv: %s\n", buf);}。《Linux网络开发必学教程》8_应用协议设计与实现。" />

《Linux网络开发必学教程》8_应用协议设计与实现

问题:下面的代码输出什么?为什么?
printf("connect success\n"); send(sock, "A", 1, 0); send(sock, "B", 1, 0); send(sock, "C", 1, 0); close(sock);

do { r = recv(client, buf, sizeof(buf), 0); if (r > 0) { printf("Recv: %s\n", buf); } }while (1); close(client);

完整代码 client.c
#include #include #include #include #include #include #include int main() { int sock = 0; struct sockaddr_in addr = {0}; int len = 0; char buf[128] = {0}; char input[32] = {0}; int r = 0; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("socket error\n"); return -1; }addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(8888); if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { printf("connect error\n"); return -1; }printf("connect success\n"); send(sock, "A", 1, 0); send(sock, "B", 1, 0); send(sock, "C", 1, 0); close(sock); return 0; }

完整代码:server.c
#include #include #include #include #include #include #include int main() { int server = 0; struct sockaddr_in saddr = {0}; int client = 0; struct sockaddr_in caddr = {0}; socklen_t asize = 0; int len = 0; char buf[32] = {0}; int r = 0; server = socket(PF_INET, SOCK_STREAM, 0); if (server == -1) { printf("server socket error\n"); return -1; }saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(8888); if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { printf("server bind error\n"); return -1; }if (listen(server, 1) == -1) { printf("server listen error\n"); return -1; }printf("server start success\n"); while (1) { asize = sizeof(caddr); client = accept(server, (struct sockaddr*)&caddr, &asize); if (client == -1) { printf("client accept error\n"); return -1; }printf("client: %d\n", client); do { r = recv(client, buf, sizeof(buf), 0); if (r > 0) { printf("Receive: %s\n", buf); } } while (r > 0); close(client); }close(server); return 0; }

输出
server start success client: 4 Receive: ABC

小知识
  • 发送缓冲区
    • 数据先进入发送缓冲区,之后由操作系统送往远端主机
  • 接收缓冲区
    • 远端数据被操作系统接受后放入接收缓冲区
    • 之后应用程序从接收缓冲区读取数据
《Linux网络开发必学教程》8_应用协议设计与实现
文章图片

TCP 应用编程中的 “问题” 数据接收端无法知道数据的发送方式
《Linux网络开发必学教程》8_应用协议设计与实现
文章图片

接收端无法知道 "ABC" 是分开发送的!
网络编程中的期望
  • 每次发送一条完整的消息,每次接收一条完整的消息
  • 即使接收缓冲区中有多条消息,也不会出现消息粘连
  • 消息中涵盖了数据类型和数据长度等信息
应用层协议设计
  • 什么是协议?
    • 协议是通讯双方为数据交换而建立的规则,标准或预定的集合
  • 协议对数据传输的作用
    • 通讯双方根据协议能够正确收发数据
    • 通讯双方根据协议能够解释数据的意义
协议设计示例
  • 目标:设计可用于数据传输的协议
  • 完整消息包含
    • 数据头:数据类型(即:数据区用途,固定长度)
    • 数据长度:数据区长度(固定长度)
    • 数据区:字节数据(变长区域)
【《Linux网络开发必学教程》8_应用协议设计与实现】《Linux网络开发必学教程》8_应用协议设计与实现
文章图片

上图可知: 消息至少 12 个字节(消息头 + 数据长度) 通过计算消息的总长度,能够避开数据粘连的问题

typedef struct { unsigned short type; unsigned short cmd; unsigned short index; unsigned short total; unsigned int length; unsigned char payload[]; // 柔性数组 }Message;

编程实验:应用层协议设计与实现
client.c
#include #include #include #include #include #include #include #include "message.h"int main() { int sock = 0; struct sockaddr_in addr = {0}; int len = 0; char buf[128] = {0}; char input[32] = {0}; int r = 0; Message *pm = NULL; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("socket error\n"); return -1; }addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(8888); if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { printf("connect error\n"); return -1; }printf("connect success\n"); pm = Message_New(0, 0, 1, 3, "A", 1); send(sock, pm, sizeof(Message) + 1, 0); pm = Message_New(0, 0, 2, 3, "B", 1); send(sock, pm, sizeof(Message) + 1, 0); pm = Message_New(0, 0, 3, 3, "C", 1); send(sock, pm, sizeof(Message) + 1, 0); close(sock); return 0; }

server.c
#include #include #include #include #include #include #include int main() { int server = 0; struct sockaddr_in saddr = {0}; int client = 0; struct sockaddr_in caddr = {0}; socklen_t asize = 0; int len = 0; char buf[32] = {0}; int r = 0; server = socket(PF_INET, SOCK_STREAM, 0); if (server == -1) { printf("server socket error\n"); return -1; }saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(8888); if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { printf("server bind error\n"); return -1; }if (listen(server, 1) == -1) { printf("server listen error\n"); return -1; }printf("server start success\n"); while (1) { asize = sizeof(caddr); client = accept(server, (struct sockaddr*)&caddr, &asize); if (client == -1) { printf("client accept error\n"); return -1; }printf("client: %d\n", client); do { r = recv(client, buf, sizeof(buf), 0); if (r > 0) { int i = 0; for (i=0; i 0); close(client); }close(server); return 0; }

输出
0000000001000300010000004100000000020003000100000042000000000300 03000100000043

思考:如何在代码层封装协议细节(仅关系消息本身)?

    推荐阅读