上海 設(shè)計網(wǎng)站建設(shè)上海百度seo優(yōu)化
如何用最簡單的方法解決TCP傳輸中的分包粘包問題?
首先需要說明一點,分包粘包等等一系列的問題并不是協(xié)議本身存在的問題,而是程序員在寫代碼的時候,沒有搞清楚數(shù)據(jù)的邊界導(dǎo)致的。
看個簡單的例子,TCP客戶端不斷的向服務(wù)器發(fā)送字符串,每次發(fā)送完成隨機(jī)睡眠一會。
char *buf[] = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbb","ccccccccccccccccccc","dddddddddddddddddddddddddddddddddddddddddddd","eeeeeeeeeeeeeeeeeeeeeeee","ffffffff","gggggggggggggggggggggggggggggggggggg","hhhhhhhhhhhhhhhhhhhhhhhhhhhhh","iii","jjjjjjj","kkkkkkkkkkkkkkkkkkkkkk"
};srand(time(NULL));for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{ if (send(sockfd, buf[i], strlen(buf[i]), 0) == -1){perror("send");break;}usleep(1000 * 10);
}
服務(wù)器端接收數(shù)據(jù)的時候同樣如此。
char buf[1024] = {0};srand(time(NULL));while (1)
{ size = recv(fd, buf, sizeof(buf), 0); if (size == -1) { perror("recv");break;} else if (size == 0){printf("客戶端斷開連接 ...\n");break;}printf("收到一條數(shù)據(jù) %s\n", buf);bzero(buf, 1024);usleep(1000 * (rand() % 100 + 1));
}
我們希望看到的現(xiàn)象是,服務(wù)器端收到的數(shù)據(jù)和客戶端一樣。
運行程序,客戶端發(fā)送完成,但是服務(wù)器端收到的數(shù)據(jù)卻不是我們想要的。
root@Turbo:test# ./1.tcp-server
等待客戶端的連接 ...
接受客戶端的連接 4
收到一條數(shù)據(jù) aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一條數(shù)據(jù) bbbbbbbbbbbcccccccccccccccccccddddddddddddddddddddddddddddddddddd
dddddddddeeeeeeeeeeeeeeeeeeeeeeeeffffffffgggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiijjjjjjj收到一條數(shù)據(jù) kkkkkkkkkkkkkkkkkkkkkk
客戶端斷開連接 ...
root@Turbo:test#
數(shù)據(jù)內(nèi)容沒有變,出現(xiàn)了多個字符串連接在一起的現(xiàn)象。
原因就是發(fā)送數(shù)據(jù)過快,或者接收數(shù)據(jù)太慢,導(dǎo)致TCP緩沖區(qū)中積累了很多數(shù)據(jù),調(diào)用recv函數(shù)讀數(shù)據(jù)的時候,就會一下子全部讀出來。
想要解決這個問題,最簡單的辦法就是分清楚數(shù)據(jù)包的邊界。發(fā)送字符串之前,在數(shù)據(jù)包的前面加上字符串的長度。
char *sendMsg = (char *)malloc(1024);
int len = 0;srand(time(NULL));for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{len = strlen(buf[i]);memcpy(sendMsg, &len, sizeof(int));memcpy(sendMsg + sizeof(int), buf[i], len);if (send(sockfd, sendMsg, strlen(buf[i]) + sizeof(int), 0) == -1){perror("send");break;}memset(sendMsg, 0, 1024);usleep(1000 * 10);
}
接收數(shù)據(jù)的時候,先讀取4個字節(jié)的整型數(shù)據(jù),得到接下來字符串的長度,再讀取對應(yīng)長度的字符串。
char buf[1024] = {0};
ssize_t size;
int len = 0;srand(time(NULL));while (1)
{ size = recv(fd, &len, sizeof(int), 0); size = recv(fd, buf, len, 0); if (size == -1) { perror("recv");break;}else if (size == 0){printf("客戶端斷開連接 ...\n");break;}printf("收到一條數(shù)據(jù) %s\n", buf);bzero(buf, 1024);usleep(1000 * (rand() % 100 + 1));
}
再次運行程序,不管睡眠時間怎么變化,服務(wù)器端收到的數(shù)據(jù)和客戶端一樣,也沒有出現(xiàn)粘在一起的現(xiàn)象。
root@Turbo:test# ./1.tcp-server
等待客戶端的連接 ...
接受客戶端的連接 4
收到一條數(shù)據(jù) aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一條數(shù)據(jù) bbbbbbbbbbb
收到一條數(shù)據(jù) ccccccccccccccccccc
收到一條數(shù)據(jù) dddddddddddddddddddddddddddddddddddddddddddd
收到一條數(shù)據(jù) eeeeeeeeeeeeeeeeeeeeeeee
收到一條數(shù)據(jù) ffffffff
收到一條數(shù)據(jù) gggggggggggggggggggggggggggggggggggg
收到一條數(shù)據(jù) hhhhhhhhhhhhhhhhhhhhhhhhhhhhh
收到一條數(shù)據(jù) iii
收到一條數(shù)據(jù) jjjjjjj
收到一條數(shù)據(jù) kkkkkkkkkkkkkkkkkkkkkk
方法很簡單,也只是加了一個包頭,其實目的就是為了告訴接收端,數(shù)據(jù)包從哪開始,到哪結(jié)束,這樣就算緩沖區(qū)中有大量數(shù)據(jù),也能分得清楚。