tcp粘包解决方案

tcp是可靠的,面向字节流的协议,传输过程中可能就会

完整的服务器端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

#define BACKLOG 10

//./a.out ip port
int main(int argc,char *argv[])
{
int sfd,cfd,ret;
struct sockaddr_in svr_addr,cli_addr;
char buffer[1024] = {0};
ssize_t sbytes = 0,rbytes = 0;
int length;
int total_received;
socklen_t len = sizeof(struct sockaddr_in);
if (argc != 3){
fprintf(stderr,"Usage : %s < ip > < port >.\n",argv[0]) ;
exit(EXIT_FAILURE);
}

//1.创建套接字
sfd = socket(AF_INET,SOCK_STREAM,0);
if (sfd == -1){
perror("[ERROR] Failed to socket.");
exit(EXIT_FAILURE);
}

bzero(&svr_addr,sizeof(struct sockaddr_in));
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(atoi(argv[2]));
svr_addr.sin_addr.s_addr = inet_addr(argv[1]);

//2.绑定ip地址与端口号
ret = bind(sfd,(const struct sockaddr *)&svr_addr,sizeof(struct sockaddr));
if (ret == -1){
perror("[ERROR] Failed to bind.");
exit(EXIT_FAILURE);
}
//3.设置监听套接字为监听状态,建立监听队列
ret = listen(sfd,BACKLOG);
if (ret == -1){
perror("[ERROR] Failed to listen.");
exit(EXIT_FAILURE);
}

//4.与客户端进行三次握手,并建立连接,默认是阻塞
cfd = accept(sfd,(struct sockaddr *)&cli_addr,&len);
if (ret == -1){
perror("[ERROR] Failed to accpet.");
exit(EXIT_FAILURE);
}

printf("ip : %s port :%d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
for(;;){
length = 0;
total_received = 0;
//接收数据的长度
rbytes = recv(cfd,&length,4,0);
if (rbytes == -1){
perror("[ERROR] Failed to recv.");
exit(EXIT_FAILURE);
}
for(;;){

rbytes = recv(cfd,buffer + total_received,length - total_received,0);
if (rbytes == -1){
perror("[ERROR] Failed to recv.");
exit(EXIT_FAILURE);
}else if (rbytes == 0){
printf("The client has been shutdown.\n");
}else if (rbytes > 0){
total_received += rbytes;
if (total_received == length)
break;
}
}
printf("buffer : %s\n",buffer);
sleep(1);
}

close(sfd);

return 0;
}

中特别要注意rbytes = recv(cfd,buffer + total_received,length - total_received,0);
这段代码的意思就是,从cfd中读取数据到buffer+已经接收的数据地址段,为什么要length+total_received,因为一段数据可能会被拆分发送,也就是说,如果只填length的话,第一次的recv可能只会接收前半段的拆分数据,那么后面的数据就接受不到。如果像上面正确设置,即使第一次没有接受完,因为total_received的值和length不等,就会在下个循环继续接收,接受的字节数就等于length-已经接受的字节数。

Contents
  1. 1. 完整的服务器端代码: