欢迎光临
我们一直在努力

C Windows非阻塞UDP通信源码,udp非阻塞

UDP通信中,recvfrom或recv等函数默认都是阻塞方式进行的,即如果没有收到消息,那么程序会一直卡在recv()这个函数这里,使得该线程不能进行后续的操作。但有时候我们需要该线程在有UDP数据发送过来的时候才进行数据接收,而在其他时间该线程还有别的任务进行处理,那么我们就需要将Sokcet设置为非阻塞通信的方式。

非阻塞通信中,需要用到select()函数,select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:

int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);

其中:

maxfd没有太大意义,一般是给赋值为最大描述符个数+1,只是起到兼容作用,大多数情况下会被系统忽略;struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫 无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合 FD_ZERO(fd_set * ),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set * ),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set* ),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。在select函数中,三个fd_set分别用于检查该文件句柄是否可读/是否可写/是否有文件错误异常。返回值大于0表示可操作,返回值等于0表示超时,返回值小于0表示有错误异常struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

下面给出一个非阻塞UDP通信的实例:

#include <WinSock2.h>#pragma comment(lib, “WS2_32”)#include <Windows.h>#include <iostream>#include <string>#define Port 5500#define CACHE_LENGTH 1024using namespace std;// 非阻塞UDP通信/************************************************************************//* 初始化Socket *//************************************************************************/bool InitWinsock(){ int Error; WORD VersionRequested; WSADATA WsaData; VersionRequested=MAKEWORD(2,2); Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2 if(Error!=0) { return FALSE; } else { if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2) { WSACleanup(); return FALSE; } } return TRUE;}int main(){ fd_set rfd; // 描述符集 这个将用来测试有没有一个可用的连接 struct timeval timeout; timeout.tv_sec=0; //等下select用到这个 timeout.tv_usec=0; //timeout设置为0,可以理解为非阻塞 char UDP_buffer[CACHE_LENGTH]; int rev=0; int SelectRcv;#pragma region Windows Socket start // Windows Socket start: BasicFunction::InitWinsock(); SOCKET sockListen; sockListen=socket(AF_INET,SOCK_DGRAM,0); if (sockListen == -1) { cout<<“Socket Fail”<<endl; return 0; } int recvbuf = 1; setsockopt(sockListen,SOL_SOCKET,SO_RCVBUF,(char*)&recvbuf,sizeof(int)); //设置为非阻塞模式 int imode=1; rev=ioctlsocket(sockListen,FIONBIO,(u_long *)&imode); if(rev == SOCKET_ERROR) { printf(“ioctlsocket failed!”); closesocket(sockListen); WSACleanup(); return -1; } struct sockaddr_in serverPCaddr; memset(&serverPCaddr,0,sizeof(sockaddr_in)); serverPCaddr.sin_family=AF_INET; serverPCaddr.sin_port=htons(Port); ///监听端口 serverPCaddr.sin_addr.s_addr=INADDR_ANY; //inet_addr(“192.168.0.11”); ///本机 if(0!= ::bind(sockListen,(struct sockaddr*)&serverPCaddr,sizeof(struct sockaddr))) { cout<<“recv_bind()失败,error: “<<GetLastError()<<endl; closesocket(sockListen); WSACleanup(); return 0; } int fromlen = sizeof(struct sockaddr_in);#pragma endregion Windows Socket start while(1) { // UDP数据接收 FD_ZERO(&rfd); //总是这样先清空一个描述符集 FD_SET(sockListen,&rfd); //把sock放入要测试的描述符集 SelectRcv = select(sockListen+1,&rfd,0,0, &timeout); //检查该套接字是否可读 if(SelectRcv<0) cout<<“监听失败”<<GetLastError()<<endl;// if(SelectRcv==0) // {// //返回值为0,表示超时,// } if (SelectRcv > 0) { memset(UDP_buffer,’\0′,(CACHE_LENGTH)*sizeof(char)); rev=0; rev=recvfrom(sockListen,UDP_buffer,(CACHE_LENGTH)*sizeof(char),0,(struct sockaddr*)&serverPCaddr,&fromlen); if (rev!=SOCKET_ERROR) { //数据接收成功,下面为对UDP接收的数据的处理 string udpstr(UDP_buffer); cout<<udpstr<<endl; } } //如果select()函数返回-1,表示无可以接收/写入的数据,那么程序就会执行下面的代码 // ……… // 代码段 // ……… }return 0;}

赞(0)
【声明】:本博客不参与任何交易,也非中介,仅记录个人感兴趣的主机测评结果和优惠活动,内容均不作直接、间接、法定、约定的保证。访问本博客请务必遵守有关互联网的相关法律、规定与规则。一旦您访问本博客,即表示您已经知晓并接受了此声明通告。