《Winsocket入门教程二:非阻塞式服务器和客户端程序.docx》由会员分享,可在线阅读,更多相关《Winsocket入门教程二:非阻塞式服务器和客户端程序.docx(14页珍藏版)》请在第一文库网上搜索。
1、Wi1ISOCket入门教程二:非阻塞式服务器和客户端程序那什么是非堵塞呢?非堵塞是相关于堵塞而言,堵塞指的是在进行一个操作的时候,如服务器接收客户端的连接(accept),服务器或者者客户端读写数据(read、write),假如该操作没有执行完成(成功或者者失败都算是执行完成),则程序会一直堵塞在操作执行的地方,直到该操作返回一个明确的结果。而非堵塞式程序则不一样,非堵塞式程序会在产生堵塞操作的地方堵塞一定的时间(该时间能够由程序员自己设置)。假如操作没有完成,在到达所设置的时间之后,不管该操作成功与否,都结束该操作而执行程序下面的操作。为了执行非堵塞操作,我们在创建了一个套接口后,需要将套
2、接口设置为非堵塞的套接口。为了将套接口设置成为非堵塞套接口,我们需要调用ioct1socket函数将套接口设置为非堵塞的套接口。ioct1socket函数的定义如下:该函数的作用是操纵套接口的I/O模式。参数s表示要设置的套接口;参数cmd表示要对该套接口设置的命令,为了要将套接口设置成为非堵塞的,我们应该填写FIONBIO;argp表示填写命令的值,如我们要将套接口设置成非堵塞的,我们需要将值设置成为1,假如我们要将套接口设置成为非堵塞状态的话,我们将值设置成为0就是了。为了进行非堵塞的操作,我们需要在进行操作之前调用se1ect函数,se1ect函数的定义如下:该函数设定一个或者多个套接口
3、的状态,并进行必要的等待,以便执行异步I/O(非堵塞)操作。参数nfds被忽略,该参数的作用仅仅是为了与伯克利套接口相兼容;参数readfds表示要检测的可读套接口的集合(该参数可选,可为设置为NU11);参数readfds表示要检测的可写套接口的集合(该参数可选,可为设置为NU11);参数exceptfds表示要检测的套接口的错误(该参数可选,可为设置为NU11);参数timeout表示执行该函数时需要等待的时间,假如为NU11则表示堵塞操作,为。则表示立即返回。下面让我们来看看参数类型fd_set,fd_set表示套接字的集合。在使用se1ect函数时,我们需要将相应的套接字加入到相应的集
4、合中。假如集合中的套接字有信号,se1eCt函数的返回值即为集合中有信号的套接字数量。我们用下面的几个宏来操作fd_set集合。我们能够使用FD_SET(sz*set)将套接字s加入到集合set中;我们能够使用FD_C1R(sr*se。将套接字S移除出集合set;我们能够使用FD_ZERO(*set)将集合Set清空;最后,我们能够使用FD_ISSET(s,*set)来推断套接字S是否在集合中有信号。接下来再让我们来看看se1ect函数的三个集合参数readfds、writefds与eceptfdSoreadfds表示可读套接字的集合,可读套接字在三种情况下有信号出现:一、假如集合中有套接字处
5、于监听状态,同时该套接字上有来自客户端的连接请求;二、假如集合中的套接字收到了Send操作发送过来的数据;三、假如集合中的套接字被关闭、重置或者者中断。writefds表示可写套接字的集合,可写套接字在两种情况下有信号出现:一、集合中的套接字通过connect操作后,连接成功;二、能够用send操作向集合中的套接字写数据。eceptfds表示错误套接字的集合,错误套接字在两种情况下有信号出现:一、集合中的套接字通过COnr1eet操作后,连接失败;二、有带外数据到来。在我们熟悉了创建服务器与客户端程序的基础知识后,我们再来看看示例程序,以加深我们对知识的懂得。程序的运行结果如下所示:下面是服务
6、器程序的代码:1. #inc1ude2. #inc1ude3. #inc1ude4. #inc1ude5. #pragmacomment(1ib,ws2-32.1ib)6. #defineASSERTassert7. usingstd:cin;8. usingstd:cout;9. usingstd:end1;10. usingstd:1ist;11. typedef1istSocket1ist;12. typedef1ist:iteratorSocket1istIterator;13. staticconstintc_iPort=10001;14. boo1GraceC1ose(SOCKET
7、*ps);15. intmain()16. 17. intiRet=SOCKET_ERROR;18. 初始化WinSOCket,所有WinSOCket程序务必先使用WSAStartUP进行初始化19. WSADATAdata;20. ZeroMemory(&data,Sizeof(WSADATA);21. iRet=WSAStartup(MAKEWORD(2z0)f&data);22. ASSERT(SOCKET_ERROR!=iRet);23. /建立服务端程序的监听套接字24. SOCKETSk1isten=INVA1ID_SOCKET;25. Sk1isten=socket(AFJNET
8、zSoCK.STREAM,O);26. ASSERT(INVA1ID_SOCKET!=Sk1isten);27. /初始化监听套接字地址信息28. sockaddr_inadrServ;/表示网络地址29. ZeroMemory(&adrServzsizeof(sockaddr-in);30. adrServ.sin-fami1y=AF_INET;/初始化地址格式,只能为AFJNET31. adrServ.sin_port=htons(c_iPort);/初始化端口,由于网络字节顺序与主机字节顺序相反,因此务必使用htons将主机字节顺序转换成网络字节顺序32. adrServ.sin_add
9、r.s_addr=INADDR_ANY;初始化IP,由因此服务器程序,因此能够将INADDR_ANY赋给该字段,表示任意的IP33. /绑定监听套接字到本地34. iRet=bind(sk1istenz(sockaddr*)&adrServzsizeof(sockaddJin);35. ASSERT(SOCKET_ERROR!=iRet);36. /使用监听套接字进行监听37. iRet=1isten(sk1istenzFD_SETSIZE);/SOMAXCONN表示能够连接到该程序的最大连接数38. ASSERT(SOCKET_ERROR!=iRet);39. coutServerbegan
10、1istening.0)sockaddr-inadrC1t;inti1en=sizeof(sockaddr-in);ZeroMemory(&adrC1t,i1en);SOCKETs=accept(sk1istenz(sockaddr*)&adrC1tz&i1en);ASSERT(INVA1ID_SOCKET!=s);s1.push_back(s);coutnServeracceptedaconnection.Thesocketis,s0)78.79.for(Socket1istIteratoriter=s1.begin();iter!=si.end0;+iter)80.81./假如有数据可读,
11、则遍历套接字列表中的所有套接字82./检测出有数据可读的套接字83.iRet=FD_ISSET(*iterz&fsRead);84.if(iRet0)85.86./读取套接字上的数据87.constintc_iBuf1en=512;88.charszBufcJBuf1en+1=,0,;89.intiRead=SOCKET_ERROR;90.iRead=recv(*iterzszBufzc_iBuf1enz0);91.if(0=iRead)/读取出现错误或者者对方关闭连接92.93.iRead=0?coutConnectionshutdownatsocketu*iterend1:94.coutC
12、onnectionrecverroratsocket,*iterend1;95.iRet=GraceC1ose(&(*iter);/假如出错则关闭套接字96.ASSERT(iRet);97.)98.e1se99.100.szBufiRead=,0,;101.coutServerreevedmessagefromsocketn*itern:nszBufend1;102./创建可写集合103.FD_SETfsWrite;104.FD_ZERO(&fsWrite);105.FD_SET(*iterz&fsWrite);106./假如有数据可写,则向客户端发送数据107.iRet=se1ect(1,NU11,&fsWritezNU11,&tv);108.if(0iRet)109.110.intiWrite=SOCKET_ERROR;111. iWrite=send(*iterzszBuf,iRead,O);112. if(SOCKET_ERROR=iWrite)113. 114. coutSendmessageerroratsocket,*iter