我们知道,网络编程的几种模型,有
1.选择模型
2.异步选择模型
3.事件选择模型
4.重叠IO模型
4完成端口模型
这些模型的目的无非就是管理多个网络的连接使其更有效率。
对于选择模型。主要用到的函数就是int select(
int nfds, //不使用
fd_set* readfds, //读请求
fd_set* writefds, //写请求
fd_set* exceptfds, //异常请求
const struct timeval* timeout //超时间隔
);
对于选择网络编程模型的主要编程步骤如下:
1. 建立套接字
2. 绑定套接字
3. 设置套接字为监听模式
4. 接受连接
5. 循环判断连接套接字上的请求。
代码如下:(udp code)
#include "stdafx.h" #include "winsock2.h" #include "windows.h" #include "tchar.h" #pragma comment(lib ,"Ws2_32.lib") int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsadata; WSAStartup(MAKEWORD(2,2) , &wsadata); if(HIBYTE(wsadata.wVersion) != 2 || LOBYTE(wsadata.wVersion) != 2) { _tprintf(TEXT("Version Error /n")); return 0; } //创建套接字 SOCKET s = socket(AF_INET , SOCK_DGRAM,0); SOCKADDR_IN addrIn; addrIn.sin_family = AF_INET; addrIn.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addrIn.sin_port = htons(8000); bind(s,(SOCKADDR*)&addrIn,sizeof(SOCKADDR)); //绑定 fd_set fd_read; int ret = 0; int nRead = 0; char szBuff[1024] = {0}; SOCKADDR_IN addrFrom; int nLen = sizeof(SOCKADDR); while(1) { FD_ZERO(&fd_read); //初始化fd_read FD_SET(s,&fd_read); //把S添加进fd_read ret = select(0,&fd_read,NULL,NULL,NULL); //选择 if(ret == SOCKET_ERROR) { _tprintf(TEXT("选择出错")); break; } if(ret > 0) { if(FD_ISSET(s,&fd_read)) //判断状态 { nRead = recvfrom(s,szBuff,1024,0,(SOCKADDR*)&addrFrom,&nLen); if(nRead == SOCKET_ERROR) { _tprintf(TEXT("接受出错")); break; } else { printf("recv : %s /n" , szBuff); } } } } closesocket(s); WSACleanup(); return 0; }
2.异步选择模型。
步骤:
1. 建立套接字
2. 绑定
3. 将套接字和窗口句柄建立关联特定网络事件
4. 设置为监听模式
5. 接受连接
6. 在窗口过程中处理建立连接的套接字的消息,比如读,写,等消息!!
7.
3.重叠IO模型
步骤:
1. 建立套接字WSASocket设置WSA_FLAG_OVERLAPPED
2. 绑定
3. 设置为监听模式
4. 接受连接
5. 接受数据WSARecv
6. 循环的等待事件触发
7. 获取事件结果WSAGetOverlappedResult
8. 对数据进行处理
9. 重新设置事件句柄
10.等待接受数据
代码:
// OverlappedSock.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "windows.h" #include "winsock2.h" #include "tchar.h" #pragma comment(lib , "Ws2_32.lib") int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsadata; WSAStartup(MAKEWORD(2,2) , &wsadata); if(LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) return 0; SOCKET s = WSASocket(AF_INET , SOCK_STREAM , IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); //创建流式套接字采用重叠IO模型 SOCKADDR_IN addrIn; addrIn.sin_addr.S_un.S_addr = inet_addr("192.168.0.48"); addrIn.sin_family = AF_INET; addrIn.sin_port = htons(5050); if(SOCKET_ERROR == bind(s ,(SOCKADDR*)&addrIn , sizeof(SOCKADDR))) //绑定套接字到本机 { return 0; } if(SOCKET_ERROR == listen(s , 5)) //将套接字设置成监听模式 { return 0; } SOCKADDR_IN addrFrom; int nLen = sizeof(addrFrom); SOCKET acceptSocket ; acceptSocket = accept(s ,(SOCKADDR*)&addrFrom , &nLen); //接受连接 if(SOCKET_ERROR == acceptSocket) { DWORD dwError = GetLastError(); return 0; } WSAEVENT wsaEvent[20]; WSAOVERLAPPED wsaOverlapped; int nEventTotal = 0; DWORD dwRecvBytes = 0; int nIndex = 0; DWORD dwTransfer = 0; DWORD dwFlags = 0; char szBuff[1024] = {0}; WSABUF wsaBuf; wsaEvent[nEventTotal] = WSACreateEvent(); //创建事件对象并设置为无信号状态 memset(&wsaOverlapped , 0 , sizeof(WSAOVERLAPPED)); wsaOverlapped.hEvent = wsaEvent[nEventTotal]; //将事件对象句柄分配给重叠IO数据结构 wsaBuf.buf = szBuff; wsaBuf.len = 1024; nEventTotal ++; WSARecv(acceptSocket , &wsaBuf ,1 , &dwRecvBytes ,&dwFlags ,&wsaOverlapped, NULL); //接受数据会立即返回关联了事件对象(因为是重叠IO模型所以会立即返回) while(1) { nIndex = WSAWaitForMultipleEvents(nEventTotal ,wsaEvent , FALSE ,WSA_INFINITE , FALSE); //等待事件被触发 WSAResetEvent(wsaEvent[nIndex - WSA_WAIT_EVENT_0]); //重置事件对象状态 WSAGetOverlappedResult(acceptSocket ,&wsaOverlapped ,&dwTransfer ,FALSE ,&dwFlags); //获取重叠IO的结果 if(dwTransfer == 0) { _tprintf(TEXT("the peer has closesocket /n")); closesocket(s); WSACloseEvent(wsaEvent[nIndex - WSA_WAIT_EVENT_0]); return 0; } printf("recv data : %s/n" , wsaBuf.buf); memset(&wsaOverlapped, 0 ,sizeof(wsaOverlapped)); wsaOverlapped.hEvent = wsaEvent[nIndex - WSA_WAIT_EVENT_0]; //重新设置事件句柄 wsaBuf.buf = szBuff; wsaBuf.len = 1024; WSARecv(acceptSocket ,&wsaBuf , 1, &dwTransfer ,NULL ,&wsaOverlapped ,NULL); //等待接受,会立即返回 } closesocket(acceptSocket); closesocket(s); WSACloseEvent(wsaEvent[0]); WSACleanup(); return 0; }