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