下面范例是一个关于非阻塞模式下的SOCKET设定处理---select模式。使用的是UDP协议。Client02首先启动,将本机的1207端口进行SOCKET绑定,并将该SOCKET模式设定为非阻塞模式, 此模式下不可直接调用recvfrom。理由: 阻塞模式下,如果直接调用recvfrom从指定的SOCKET读取数据,如果还没有接受到来自Client01的数据,函数recvfrom会一直等待,直到有数据可以读出为止。当然,也可以创建一个独立的线程来专门调用recvfrom,这样可以不影响主线程的处理。非阻塞模式下,recvfrom函数会立即返回,如果此时没有数据到达,该函数调用必然失败,返回值为WSAEWOULDBLOCK,表明当前状态是非阻塞模式的调用,且没有数据到达。这时,通常的做法是,创建一个独立的线程调用recvfrom,判断返回值(如果是WSAEWOULDBLOCK)进行循环调用recvfrom,直到有数据到达为止,这样可以,但是开销大,不好。如果,用MSG_PEEK来查询,其实开销也很大,也不推荐。 或者用select模式进行处理。(select应该是专门用来对付非阻塞模式的一种办法)select函数察看要求进行处理的SOCKET, 同时设有超时Value, 这个函数本身会等待(也就是本身其实是个阻塞的函数),直到有可用的SOCKET为止,然后,进行处理,再进行select等待,如此反复执行。 //////////////////////////////////////////////////////////////////// // // UDP Client 02 // IP: 127.0.0.1 // PORT: 1207 // ////////////////////////////////////////////////////////////////////#define CONNECT_IP "127.0.0.1" #define CONNECT_PORT 1207 #define DEFAULT_BUFFER_LEN 256char* getBlockMode(int nMode) { return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ; } int _tmain(int argc, _TCHAR* argv[]) { printf(">>>>>UDP Client启动<<<<<<<\n"); WSAData wsData; SOCKET sClient; SOCKADDR_IN addrSender; SOCKADDR_IN addrRecviver; int addrSenderLen = sizeof(addrSender); char sendBuff[DEFAULT_BUFFER_LEN] = {0}; char recvBuff[DEFAULT_BUFFER_LEN] = {0}; DWORD nMode = 0; int nError = -1; printf("-启动SOCKET库\n"); WSAStartup( MAKEWORD(2,2), &wsData ); addrRecviver.sin_family = AF_INET; addrRecviver.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP ); addrRecviver.sin_port = htons( CONNECT_PORT ); printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrRecviver.sin_addr), ntohs(addrRecviver.sin_port)); printf("-创建SOCKET\n"); sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP ); if( sClient == INVALID_SOCKET ) { printf("!!! socket failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } //////////////////////// //阻塞/非阻塞模式设定 //////////////////////// nMode = 1; //阻塞模式/非阻塞模式 nError = ioctlsocket( sClient, FIONBIO, &nMode ); if( nError!=0 ) { printf("!!! ioctlsocket failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } printf("-设定SOCKET模式: %s\n", getBlockMode(nMode)); printf("-绑定SOCKET用于接受数据\n"); nError = bind( sClient, (const sockaddr *)&addrRecviver, sizeof(addrRecviver) ); if( nError == SOCKET_ERROR ) { printf("!!! bind failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } //////////////////////////////////////////// // // select 模型用于监视制定socket 非阻塞模式设定中 // //////////////////////////////////////////// fd_set fdRead; struct timeval tv = {5,0}; //{second, milisec} while( true ) { FD_ZERO( &fdRead ); //首先清空fd集 FD_SET( sClient, &fdRead ); //加入要查看的SOCKET printf("-select 监视中\n"); nError = select( 0, &fdRead, NULL, NULL, &tv ); //调用select进行SOCKET查看 ;;;;;if( nError == 0 ) { printf("-!!! select 超时: %d sec\n", tv.tv_sec); continue; } else if( nError < 0 ) { printf("!!! select failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } printf("-select 查找可读的SOCKET\n"); if( FD_ISSET(sClient, &fdRead) ) //查找可用的SOCKET,不可用的SOCKET会从fd集里删除掉 {//可读入数据了 printf("-找到一个可以读入的SOCKET\n"); nError = recvfrom( sClient, recvBuff, sizeof(recvBuff), 0, (sockaddr *)&addrSender, &addrSenderLen ); if( nError == SOCKET_ERROR ) { printf("!!! recvfrom failed: %d\n", WSAGetLastError()); closesocket( sClient ); WSACleanup(); return -1; } printf("-收到数据: %s [%s:%d]\n", recvBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port)); } else{ printf("-没有找到一个可以读入的SOCKET\n"); } }//while(true) printf("-Socket关闭\n"); closesocket( sClient ); WSACleanup();return 0; } //////////////////////////////////////////////////////////////////// // // UDP Client 01 // IP: 127.0.0.1 // PORT: 1207 // ////////////////////////////////////////////////////////////////////#define CONNECT_IP "127.0.0.1" #define CONNECT_PORT 1207 #define DEFAULT_BUFFER_LEN 256char* getBlockMode(int nMode) { return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ; } int _tmain(int argc, _TCHAR* argv[]) { printf(">>>>>UDP Client启动<<<<<<<\n"); WSAData wsData; SOCKET sClient; SOCKADDR_IN addrSender; SOCKADDR_IN addrRecvier; int addrRecvierLen = sizeof(addrRecvier); char sendBuff[DEFAULT_BUFFER_LEN] = {0}; char recvBuff[DEFAULT_BUFFER_LEN] = {0}; DWORD nMode = 0; int nError = -1; printf("-启动SOCKET库\n"); WSAStartup( MAKEWORD(2,2), &wsData ); addrSender.sin_family = AF_INET; addrSender.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP ); addrSender.sin_port = htons( CONNECT_PORT ); printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port)); printf("-创建SOCKET\n"); sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP ); if( sClient == INVALID_SOCKET ) { printf("!!! socket failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } //////////////////////// //阻塞/非阻塞模式设定 //////////////////////// nMode = 0; //阻塞模式/非阻塞模式 nError = ioctlsocket( sClient, FIONBIO, &nMode ); if( nError!=0 ) { printf("!!! ioctlsocket failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } printf("-设定SOCKET模式: %s\n", getBlockMode(nMode)); strcpy( sendBuff, "message from Client01" ); printf("-送信设定: %s [%s:%d]\n", sendBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port)); nError = sendto( sClient, sendBuff, sizeof(sendBuff), 0, (const sockaddr *)&addrSender, sizeof(addrSender) ); if( nError == SOCKET_ERROR ) { printf("!!! sendto failed: %d\n", WSAGetLastError()); WSACleanup(); return -1; } printf("-Socket关闭\n"); closesocket( sClient ); WSACleanup();return 0; } |
相关文章
- 嵌入式学习37-TCP并发模型-有限 2.IO模型: 1.阻塞IO: 没有数据到来时,可以让任务挂起 节省CPU资源开销,提高系统效率 2.非阻塞IO: 程序未接收到数据时一直执行 效率很低 3.异步IO 只能绑定一个文件描述符用来 读取数据 4.多路复用IO select 1.select监听的集合中的文件描述符有 上限限制 2.select有 内核层 向 用户层数据空间 拷贝 的过程,占用系统资源开销 3.select必须 轮询检测 产生 事件 的文件描述符 4.select 只能工作 在 水平触发 模式(低速模式) 无法工作 在 边沿触发 模式(高速模式) poll (监听的集合中的文件描述符有 没有上限限制) 1.poll有 内核层 向 用户层 数据空间 拷贝 的过程,占用系统资源开销 2.poll必须 轮询检测 产生 事件 的文件描述符 3.poll 只能工作在水平触发模式(低速模式) 与select相同 无法工作在边沿触发(高速模式) 3.函数接口: 1.select int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 功能: select 监听 文件描述符集合 中 是否 有文件描述编程 ready状态 select 监听 文件描述符集合 中 ,若有状态 , 将没有ready状态的T除 若无状态,将阻塞继续等待 参数: nfds: 最大文件描述符的值 +1 readfds: 读 文件描述符集合 writefds: 写 文件描述符集合 exceptfds: 其余 文件描述符集合 timeout: 等待的时长 NULL 一直等待(超时处理) 返回值: 成功 返回 文件描述符集合中 的 文件描述符个数 失败 返回 -1 void FD_CLR (int fd, fd_set *set); 功能: 将文件描述符 fd 从集合中清除
- linux下socket编程 select实现非阻塞模式多台客户端与服务器通信
- 关于非阻塞模式下的SOCKET设定处理---select模式