6.win32網路程式設計(二).recv函式的緩衝區大小的問題
阿新 • • 發佈:2019-02-11
繼續之前的在VC++上的win32網路程式設計版本,這次試試在VS上執行:
開發環境:VS2015 和 VS2013
這次在跑之前的程式碼的時候,發現有幾個bug:
1.
客戶端並沒有send,服務端會繼續呼叫recv函式(而不是被阻塞),接收到一大片的空字元緩衝
發現好像是伺服器的緩衝區大小大於客戶端的,所以會呼叫recv函式2次
其實看到百度百科中對recv這個函式的解釋中說到:
(注意協議接收到的資料可能大於buf的長度,所以在這種情況下要呼叫幾次recv函式才能把s的接收緩衝中的資料copy完。recv函式僅僅是copy資料,真正的接收資料是協議來完成的)
沒想到自己親身遇到這個bug!
2.
不知道為什麼有一行程式碼,對buf[n]字元陣列的[n]賦值為0————導致溢位
但是不太理解為什麼之前在VC++中沒有報錯,所以最後註釋掉這行之後,就可以繼續跑了。
客戶端程式碼如下:
// cilent.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <WinSock2.h> #include <WS2tcpip.h> #include <cstdio> #include <iostream> using namespace std; #pragma comment(lib,"ws2_32.lib") //如果你用c語言,你需要通過 #pragma comment();命令來連線靜態庫。lib關鍵字表示連入一個庫檔案 int main() { WORD sockVersion = MAKEWORD(2, 2); WSADATA data; if (WSAStartup(sockVersion, &data) != 0) { return 0; } /* ---- 1、建立套接字(socket) ------ */ SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sclient == INVALID_SOCKET) { printf("invalid socket !"); return 0; } /* ---- 2、向伺服器傳送連線請求 ------ */ struct in_addr addrStru; //存放一個ip v4地址 sockaddr_in serAddr; serAddr.sin_family = AF_INET; serAddr.sin_port = htons(8899); inet_pton(AF_INET, "172.18.58.50", (void *)&addrStru); serAddr.sin_addr = addrStru; if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) { cout << WSAGetLastError() << endl; printf("connect error!\n"); closesocket(sclient); } /* ---- 3、、假如連線成功,實現互相通訊 ------ */ char buffer[1000]; while (1) { memset(buffer, 0, sizeof(buffer)); scanf_s("%s", &buffer, sizeof(buffer)); if (buffer[0] == 'e') break;//結束暗號 else printf("%s\n", buffer); printf("%d\n", sizeof(buffer)); /* ---- 4、向服務端傳送資料 ------ */ send(sclient, buffer, sizeof(buffer), 0); printf("send data\n"); /*----- 5. 接受服務端回傳的資料--------*/ memset(buffer, 0, sizeof(buffer)); int ret = recv(sclient, buffer, sizeof(buffer), 0); if (ret > 0) { //recv函式返回其實際copy的位元組數,如果recv函式在等待協議接收資料時網路中斷了,那麼它返回0。 //buffer[ret] = 0x00; ----------------報錯的根源 printf(buffer); } else { printf("recv error!\n"); } } printf("hello\n"); return 0; }
服務端程式碼如下:
// server.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include<WinSock2.h> #include<WS2tcpip.h> #include<iostream> using namespace std; #pragma comment(lib,"Ws2_32.lib") //包含監聽與連線兩種功能的Socket #define MYLOCAL "172.18.58.50" int main() { WORD sockVersion = MAKEWORD(2, 2); WSADATA wsadata; if (WSAStartup(sockVersion, &wsadata) != 0) return 0; /*--------------------1,建立套接字-------------------*/ SOCKET sserver = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); //套接字號 if (sserver == INVALID_SOCKET) { printf("invalid socket!\n"); return 0; } bool l = TRUE; //這個l有什麼用呢? setsockopt(sserver, SOL_SOCKET, SO_REUSEADDR, (char *)&l, sizeof(l)); //用於任意型別、任意狀態套介面的設定選項值 /*------ 2、將套接字繫結到本地ip地址和一個埠上(bind) ----- */ SOCKADDR_IN localhost; struct in_addr addrStru; //存放一個ip v4地址 inet_pton(AF_INET, MYLOCAL , (void *)&addrStru); localhost.sin_family = AF_INET; localhost.sin_port = htons(8899); localhost.sin_addr = addrStru; bind(sserver,(sockaddr *)&localhost, sizeof(localhost)); //將此套接字和本地地址下的8899埠繫結 /*------------------3.監聽-----------------------------*/ listen(sserver, 100);//作用是將用sock建立的主動套介面轉換成被動套介面,並等待來自客戶端的連線請求 //版本1.0,先假設這是一個 點對點的連線,最大連線數目是1個 char buffer[1024]; int client_st = 0;//client端socket struct sockaddr_in client_addr;//表示client端的ip地址 memset(&client_addr, 0, sizeof(client_addr)); socklen_t len = sizeof(client_addr); /*------------------4.接收訊息-----------------------------*/ /* 5. accept函式:accept函式由TCP伺服器呼叫,從已完成連線佇列頭返回一個已完成連線, 如果完成連線佇列為空,則程序進入睡眠狀態。 */ //accept會阻塞,直到有客戶端連線過來,accept返回client的socket描述符 client_st = accept(sserver, (struct sockaddr *)&client_addr, &len); //說好的會阻塞呢???? char IPdotdec[20]; inet_ntop(AF_INET, (void *)&(client_addr.sin_addr), IPdotdec, 16); printf("accept by %s\n", IPdotdec); while (1) { memset(buffer, 0, sizeof(buffer)); int rc = recv(client_st, buffer, sizeof(buffer), 0);//recv是阻塞呼叫 Sleep(5); printf("I receive %d\n",rc); printf("sizeof(buffer) is %d\n", sizeof(buffer)); if (rc == 0) { printf("接受資訊異常!\n"); return 0; } printf("接收到資訊:%s!\n", buffer); scanf_s("%s", &buffer, sizeof(buffer)); if (buffer[0] == 'e') break; //結束暗號 /*------------------5.傳送訊息-----------------------------*/ send(client_st, buffer, sizeof(buffer), 0); printf("I send!\n\n"); } /*------------------6.關閉套接字--------------------*/ closesocket(sserver); return 0; }
實現的效果還是蠻簡單的,其實就是2個控制檯視窗可以互相通訊,但是因為只有一個主執行緒,是序列的,會被recv阻塞,只能依次傳送,接受,傳送接收。
下一次,將探討多執行緒下的網路程式設計。