[Win32] Windows Sockets 2笔记(3)TCP Server端的实现(长数据接收+超时机制)
2015-09-01 13:57
639 查看
本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:/article/9672548.html
在上一篇中,我说了Winsock2的基本函数,这一篇,我们来制作一个TCP Server端,并且要求这个TCP Server端要支持长数据接收和接收超时机制
由于函数已经讲过了,所以socket函数不再重复,请见上一篇:[Win32] Windows Sockets 2笔记(2)基本函数。地址:/article/9672547.html
超时机制比较容易,就是select函数,详细见上一篇
要想实现长数据接收,因为双方无法得知对方要发送多长的数据,同时,发送和接收都是协议完成的,socket只是进行了简单的缓冲区复制,因此无论是recv一次,还是循环recv直到出错,都是不可取的。
有两种解决方法:
方法1。指定一个结束符,一直接收到出现结束符为止,这就要求正文中不能有这个结束符,如果有必须替换或者编码
方法2。指定一个应用层协议,或自己设计一个应用层协议,能让对方预先知道你要发多长的包,比如HTTP就在协议头里有一个Content-length字段,可以让对方知道数据包有多长。
在本例中,我将采用方法2,并预定一个协议,从数据包开头计算,直到出现第一个“@”字符,这个字符之前的数据是数据长度。
源代码如下:
在上一篇中,我说了Winsock2的基本函数,这一篇,我们来制作一个TCP Server端,并且要求这个TCP Server端要支持长数据接收和接收超时机制
由于函数已经讲过了,所以socket函数不再重复,请见上一篇:[Win32] Windows Sockets 2笔记(2)基本函数。地址:/article/9672547.html
超时机制比较容易,就是select函数,详细见上一篇
要想实现长数据接收,因为双方无法得知对方要发送多长的数据,同时,发送和接收都是协议完成的,socket只是进行了简单的缓冲区复制,因此无论是recv一次,还是循环recv直到出错,都是不可取的。
有两种解决方法:
方法1。指定一个结束符,一直接收到出现结束符为止,这就要求正文中不能有这个结束符,如果有必须替换或者编码
方法2。指定一个应用层协议,或自己设计一个应用层协议,能让对方预先知道你要发多长的包,比如HTTP就在协议头里有一个Content-length字段,可以让对方知道数据包有多长。
在本例中,我将采用方法2,并预定一个协议,从数据包开头计算,直到出现第一个“@”字符,这个字符之前的数据是数据长度。
源代码如下:
#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, connectsock; sockaddr_in addr, clientaddr; 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 = INADDR_ANY;//inet_addr("127.0.0.1"); if (bind(Socket, (sockaddr*)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR) { MessageBox(NULL, TEXT("Socket绑定到本机地址失败"), TEXT("错误"), MB_ICONERROR); exit(1); } if (listen(Socket, SOMAXCONN) == SOCKET_ERROR) { MessageBox(NULL, TEXT("Socket监听失败"), TEXT("错误"), MB_ICONERROR); exit(1); } char msg[1001]; int readlen = 0; int addrlen = sizeof(sockaddr_in); char* ip=NULL; while (1){ connectsock = accept(Socket, (sockaddr*)&clientaddr, &addrlen); if (connectsock == INVALID_SOCKET)continue; RtlZeroMemory(msg, 1001); ip=inet_ntoa(clientaddr.sin_addr);//对方IP地址 if (!WaitRecv(connectsock, 2)){//等待接收 closesocket(connectsock); continue;//超时,关闭套接字并继续接收 } int r = recv(connectsock, 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(connectsock, 2))break;//超时 r = recv(connectsock, msg, 1000, 0); if (r <= 0)break; readlen += r; MessageBoxA(NULL, msg, "收到数据", 0); } } else{ closesocket(connectsock); } } MessageBoxA(NULL, "数据接收完成", ip, 0); send(connectsock, msg, strlen(msg), 0); closesocket(connectsock); } return 0; }
相关文章推荐
- Linux网络编程echo简单示例1
- Android 网络编程——Socket,HTTP,SOAP辨析
- [科普文] 网络流量劫持危害有多大?继续解密黑客巧妙神奇的“离线”入侵手段 (二)
- 无线网络覆盖
- UVA - 12264 Risk (二分,网络流)
- 用java发送Http请求,并对chunked解码的介绍
- Java基础---网络编程
- nginx处理HTTP header问题
- Android 网络编程——SOAP
- Python脚本之TCP端口检查
- TCP连接
- ASP.NET 防盗链的实现[HttpHandler]
- 8月第3周网络安全报告:.COM网站73.2%被篡改
- HTTP POST GET 本质区别详解
- 2MSL TCP三次握手连接与四次握手关闭过程
- TCP协议三次握手连接四次握手断开和DOS攻击
- 跨域http请求
- Altium Designer笔记之放置网络标号时快速确定名称
- HTTP/TCP/UDP协议详解
- 确认网络环境