c++ socket学习(1.4)
生活随笔
收集整理的這篇文章主要介紹了
c++ socket学习(1.4)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文學習相關資料:
C/C++ socket編程教程
環境:vs2015
源碼:本文代碼
前面學到了TCP怎么循環發包,但是TCP連接的話會出現一個問題粘包。
TCP連接接收到的數據并不是馬上讀取到內存里面的,而是放在緩沖區,讓后調用recv函數來從緩沖區讀取數據。
當然緩沖區是有大小限制
這時候就可能會出現粘包了。
1、假如客戶端發送的數據很少,但次數多;服務端一次讀取得多,就會將多次發送的內容全部讀到一起。
2、假如一次客戶端發送的數據很多,服務端一次沒有讀取完,那么就會還有剩下的數據在緩沖區;這時客戶端第二次發送數據過來,和前一次讀剩下的數據一起放。這時服務端又來讀取數據了,就會出現了第一次讀剩下的數據和第二次的部分數據被服務端一起讀取。
來看看怎么實現這樣的情況
情況1:
服務端
int maxlen = 200; //接受客戶端的連接 SOCKET client = accept(servSock, (sockaddr*)&clntAddr, &nSize);while (1) {//通過sleep來讓客戶端信息全部發送到緩沖區,讓服務器能夠一次讀取完Sleep(1000);//接受到信息int len = recv(client, buf, maxlen, 0);std::string s(buf);if (s.compare("exit") == 0) {std::cout << "接收到關閉信息,關閉服務器" << std::endl;break;}std::cout << s << " " << len << std::endl;}closesocket(client);客戶端
int num = 5; connect(client, (sockaddr*)&servAddr, sizeof(sockaddr));std::string s("1234"); for (int i = 0; i < num; ++i)std::cout << send(client, s.c_str(), s.size(), 0) << std::endl;//注意不要把字符串最后面那個'\0'也發送了 send(client, "\0", 1, 0); //讓服務端先讀取完前面的內容在發送結束 Sleep(4000); s = "exit"; std::cout << send(client, s.c_str(), s.size() + 1, 0) << std::endl;closesocket(client);情況2:
服務端
int maxlen = 5; //注意這里不同了,表示服務端一次讀取得少 //接受客戶端的連接 SOCKET client = accept(servSock, (sockaddr*)&clntAddr, &nSize);while (1) {//通過sleep來讓客戶端信息全部發送到緩沖區,讓服務器能夠一次讀取完//Sleep(1000); 睡眠也注釋了//接受到信息int len = recv(client, buf, maxlen, 0);std::string s(buf);if (s.compare("exit") == 0) {std::cout << "接收到關閉信息,關閉服務器" << std::endl;break;}std::cout << s << " " << len << std::endl;}closesocket(client);客戶端
int num = 5; connect(client, (sockaddr*)&servAddr, sizeof(sockaddr));std::string s("12345679"); //這里不同了,表示客戶端一次發送得多 for (int i = 0; i < num; ++i)std::cout << send(client, s.c_str(), s.size(), 0) << std::endl;//注意不要把字符串最后面那個'\0'也發送了 send(client, "\0", 1, 0); //讓服務端先讀取完前面的內容在發送結束,不然會讀到最后面那個'\0',和exit拼在了一起,就不會結束了 Sleep(4000); s = "exit"; std::cout << send(client, s.c_str(), s.size() + 1, 0) << std::endl;closesocket(client);怎么解決粘包呢?
1、約定好每次讀取的數據長度,每次發送的數據長度,保證一次讀完
2、約定好每段發送數據的結束符,當讀到這個結束符的時候表明讀取完了第一次發送的數據,結束符后面的內容屬于第二次發送的數據。
方法一比較簡單,就只是改個數值就可以了
來看看方法二要怎么做:
客戶端
int num = 5; connect(client, (sockaddr*)&servAddr, sizeof(sockaddr));std::string s("12345679A"); //這里確定每次發送的數據長度為10字節,A表示數據結束 for (int i = 0; i < num; ++i)std::cout << send(client, s.c_str(), s.size(), 0) << std::endl; Sleep(4000); s = "exit"; std::cout << send(client, s.c_str(), s.size() + 1, 0) << std::endl;closesocket(client);服務端
SOCKET client = accept(servSock, (sockaddr*)&clntAddr, &nSize); char temp[maxlen]; memset(temp, 0, sizeof(temp)); //通過sleep來讓客戶端信息全部發送到緩沖區,讓服務器能夠一次讀取完 Sleep(1000);//表示讀取緩沖區的內容的下標 int vBufSite = 0; int len = recv(client, buf, 7, 0); //一次讀7個字節,肯定是要讀兩次的 while (1) {int vTempSite = 0;bool vRead = true;while (vRead) {if (vBufSite >=len) { //看看前一次讀取的內容是不是包含了兩次發送的內容len = recv(client, buf, 7, 0);vBufSite = 0;}while(vBufSite < len ) { //看看是不是包含了兩次發送的內容if (buf[vBufSite] == 0) {vRead = false;temp[vTempSite] = 0;++vBufSite;break;}else if (buf[vBufSite] != 'A') { //讀到分隔符就結束temp[vTempSite++] = buf[vBufSite];++vBufSite;}else {vRead = false;temp[vTempSite] = 0;++vBufSite;break;}}}std::string s(temp);if (s.compare("exit") == 0) {std::cout << "接收到關閉信息,關閉服務器" << std::endl;break;}std::cout << s << std::endl;temp[0] = 0; } closesocket(client);總結
以上是生活随笔為你收集整理的c++ socket学习(1.4)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天猫开店需要多少钱啊?
- 下一篇: c++ socket学习(1.5)