1. 程式人生 > >6.win32網路程式設計(二).recv函式的緩衝區大小的問題

6.win32網路程式設計(二).recv函式的緩衝區大小的問題

繼續之前的在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阻塞,只能依次傳送,接受,傳送接收。

下一次,將探討多執行緒下的網路程式設計。