问题:下面的代码输出什么?为什么?
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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.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;
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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
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
小知识
发送缓冲区
- 数据先进入发送缓冲区,之后由操作系统送往远端主机
接收缓冲区
- 远端数据被操作系统接受后放入接收缓冲区
- 之后应用程序从接收缓冲区读取数据
TCP 应用编程中的 “问题”
数据接收端无法知道数据的发送方式
接收端无法知道 "ABC"
是分开发送的!
网络编程中的期望
- 每次发送一条完整的消息,每次接收一条完整的消息
- 即使接收缓冲区中有多条消息,也不会出现消息粘连
- 消息中涵盖了数据类型和数据长度等信息
应用层协议设计
什么是协议?
- 协议是通讯双方为数据交换而建立的规则,标准或预定的集合
协议对数据传输的作用
- 通讯双方根据协议能够正确收发数据
- 通讯双方根据协议能够解释数据的意义
协议设计示例
- 目标:设计可用于数据传输的协议
完整消息包含
- 数据头:数据类型(即:数据区用途,固定长度)
- 数据长度:数据区长度(固定长度)
- 数据区:字节数据(变长区域)
上图可知:
消息至少 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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
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<r; ++i) {
printf("%02x", buf[i]);
}
printf("\n");
}
} while (r > 0);
close(client);
}
close(server);
return 0;
}
输出
0000000001000300010000004100000000020003000100000042000000000300
03000100000043
思考:如何在代码层封装协议细节(仅关系消息本身)?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。