[Win32] Windows Sockets 2笔记(4)TCP Client端的实现(长数据接收+超时机制)

 本博文由CSDN博主zuishikonghuan所作,版权归zuishiko nghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/48047339

在上上篇中,我说了Winsock2的基本函数,上一篇,我们制作了一个TCP Server端,并且实现了TCP Server端要支持长数据接收和接收超时机制(地址:http://blog.csdn.net/zuishikonghuan/article/details/48027823)

这一篇呢,就再写一个TCP Client端的实现,同样要求这个TCP Client端要支持长数据接收和接收超时机制

由于函数已经讲过了,所以socket函数不再重复,请见上一篇:[Win32] Windows Sockets 2笔记(2)基本函数。地址:http://blog.csdn.net/zuishikonghuan/article/details/48001135

超时机制比较容易,就是select函数,详细见上一篇

要想实现长数据接收,因为双方无法得知对方要发送多长的数据,同时,发送和接收都是协议完成的,socket只是进行了简单的缓冲区复制,因此无论是recv一次,还是循环recv直到出错,都是不可取的。

有两种解决方法:

方法1。指定一个结束符,一直接收到出现结束符为止,这就要求正文中不能有这个结束符,如果有必须替换或者编码

方法2。指定一个应用层协议,或自己设计一个应用层协议,能让对方预先知道你要发多长的包,比如HTTP就在协议头里有一个Content-length字段,可以让对方知道数据包有多长。

在本例中,我将采用方法2,并预定一个协议,从数据包开头计算,直到出现第一个“@”字符,这个字符之前的数据是数据长度。

Server端可以这样发送长数据:

	send(Socket, "5005@", 5, 0);
	char msg[1001] = { 0 };
	RtlFillMemory(msg, 1000, 'A');
	for (int i = 0; i < 5; i++)
		send(Socket, msg, strlen(msg), 0);

Client端源代码如下:

#include <winsock2.h>
#pragma comment(lib , "Ws2_32.lib")

#include <Windows.h>

//等待接收操作
//参数1:套接字描述符 参数2:等待的秒数
BOOL WaitRecv(SOCKET s, int timeout){
	fd_set nfds;
	RtlZeroMemory(&nfds, sizeof(fd_set));
	nfds.fd_array[0] = s;
	nfds.fd_count = 1;
	timeval time = { timeout, 0 };
	if (select(0, &nfds, NULL, NULL, &time) <= 0){
		return FALSE;
	}
	return TRUE;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int  nCmdShow)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);

	SOCKET Socket;

	sockaddr_in addr;

	if (WSAStartup(wVersionRequested, &wsaData) != 0){
		MessageBox(NULL, TEXT("Winsock开启失败"), TEXT("错误"), MB_ICONERROR);
		return 1;
	}

	Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (Socket == INVALID_SOCKET)
	{
		MessageBox(NULL, TEXT("Socket创建失败"), TEXT("错误"), MB_ICONERROR);
		return 1;
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons(8888);
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//服务器地址


	if (connect(Socket, (sockaddr*)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR)
	{
		MessageBox(NULL, TEXT("Socket连接到服务器失败"), TEXT("错误"), MB_ICONERROR);
		exit(1);
	}

	send(Socket, "2@", 2, 0);

	char msg[1001] = { 0 };
	int readlen = 0;
	int addrlen = sizeof(sockaddr_in);
	RtlZeroMemory(msg, 1001);

	if (!WaitRecv(Socket, 12)){//等待接收
		closesocket(Socket);
		return 0;
	}

	int r = recv(Socket, msg, 1000, 0);
	int len = 0;
	if (r > 0){
		unsigned int x = (unsigned int)strstr(msg, "@");//找到第一个出现@的位置,按照预先的协议,@之前是完整的数据长度
		if (x != 0){
			msg[x - (unsigned int)msg] = '\0';//将@换成字符串结束符
			len = atoi(msg);//获取长度
			msg[x - (unsigned int)msg] = '@';//替换回来
			readlen = r;//已经读取的字节
			MessageBoxA(NULL, msg, "收到数据", 0);

			while (readlen < len){//一直接收,直到达到我们认为的长度
				RtlZeroMemory(msg, 1001);
				if (!WaitRecv(Socket, 2))break;//超时
				r = recv(Socket, msg, 1000, 0);
				if (r <= 0)break;
				readlen += r;
				MessageBoxA(NULL, msg, "收到数据", 0);
			}
		}
		else{
			closesocket(Socket);
		}
	}
	MessageBoxA(NULL, "数据接收完成", "", 0);
	closesocket(Socket);

	return 0;
}



你可能感兴趣的:(windows,Win32,api,winsock2,winapi)