已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Winsocket 入门教程二:非阻塞式服务器和客户端程序(TCP) 收藏 上次为大家介绍了阻塞式多线程服务端程序和阻塞式客户端程序的设计方法,但是 在上文的最后也提到过,服务器程序会因为建立连接和关闭连接而频繁的创建和关闭线程 会产生大量的内存碎片,从而导致服务端程序不能保证长时间的稳定运行。因此我在这里 为大家介绍另外一种建立服务器和客户端程序的方法,即建立非阻塞式的服务器和客户端 程序。 那什么是非阻塞呢?非阻塞是相对于阻塞而言,阻塞指的是在进行一个操作的时候 ,如服务器接收客户端的连接(accept),服务器或者客户端读写数据 (read、write),如 果该操作没有执行完成(成功或者失败都算是执行完成) ,则程序会一直阻塞在操作执行的 地方,直到该操作返回一个明确的结果。而非阻塞式程序则不一样,非阻塞式程序会在产 生阻塞操作的地方阻塞一定的时间(该时间可以由程序员自己设置)。如果操作没有完成, 在到达所设置的时间之后,无论该操作成功与否,都结束该操作而执行程序下面的操作。 为了执行非阻塞操作,我们在创建了一个套接口后,需要将套接口设置为非阻塞的 套接口。为了将套接口设置成为非阻塞套接口,我们需要调用 ioctlsocket 函数将套接口 设置为非阻塞的套接口。ioctlsocket 函数的定义如下: int ioctlsocket( SOCKET s, long cmd, u_long FAR *argp ) 该函数的作用是控制套接口的 I/O 模式。 参数 s 表示要设置的套接口;参数 cmd 表示要对该套接口设置的命令,为了要将套 接口设置成为非阻塞的,我们应该填写 FIONBIO;argp 表示填写命令的值,如我们要将 套接口设置成非阻塞的,我们需要将值设置成为 1,如果我们要将套接口设置成为非阻塞 状态的话,我们将值设置成为 0 就是了。 为了进行非阻塞的操作,我们需要在进行操作之前调用 select 函数,select 函数的 定义如下: int select(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout ); 该函数设定一个或多个套接口的状态,并进行必要的等待,以便执行异步 I/0(非 阻塞)操作。 参数 nfds 被忽略,该参数的作用仅仅是为了与伯克利套接口相兼容;参数 readf ds 表示要检测的可读套接口的集合(该参数可选,可为设置 为 NULL);参数 readfds 表示要检测的可写套接口的集合(该参数可选,可为设置为 NUL L);参数 exceptfds 表示要检测的套接口的错误( 该参数可选,可为设置为 NULL);参数 timeout 表示执行该函数时需要等待的时间,如果为 NULL 则表示阻塞操作,为 0 则表示 立即返回。 下面让我们来看看参数类型 fd_set,fd_set 表示套接字的集合。在使用 select 函数 时,我们需要将相应的套接字加入到相应的集合中。如果集合中的套接字有信号,select 函数的返回值即为集合中有信号的套接字数量。 我们用下面的几个宏来操作 fd_set 集合。我们可以使用 FD_SET(s, *set)将套接 字 s 加入到集合 set 中;我们可以使用 FD_CLR(s, *set)将套接字 s 移除出集合 set; 我们可以使用 FD_ZERO(*set)将集合 set 清空;最后,我们可以使用 FD_ISSET(s, *set)来判断套接字 s 是否在集合中有信号。 接下来再让我们来看看 select 函数的三个集合参数 readfds、writefds 以及 excep tfds。 readfds 表示可读套接字的集合,可读套接字在三种情况下有信号出现:一、如果集 合中有套接字处于监听状态,并且该套接字上有来自客户端的连接请求;二、如果集合中 的套接字收到了 send 操作发送过来的数据;三、如果集合中的套接字被关闭、重置或者 中断。 writefds 表示可写套接字的集合,可写套接字在两种情况下有信号出现:一、集合 中的套接字经过 connect 操作后,连接成功;二、可以用 send 操作向集合中的套接字写 数据。 exceptfds 表示错误套接字的集合,错误套接字在两种情况下有信号出现:一、集合 中的套接字经过 connect 操作后,连接失败;二、有带外数据到来。 在我们了解了创建服务器和客户端程序的基础知识后,我们再来看看示例程序,以加 深我们对知识的理解。 程序的运行结果如下所示: 下面是服务器程序的代码: view plaincopy to clipboardprint? 1. #include 2. #include 3. #include 4. #include 5. #pragma comment(lib, “ws2_32.lib“) 6. #define ASSERT assert 7. using std:cin; 8. using std:cout; 9. using std:endl; 10. using std:list; 11. typedef list SocketList; 12. typedef list:iterator SocketListIterator; 13. static const int c_iPort = 10001; 14. bool GraceClose(SOCKET *ps); 15. int main() 16. 17. int iRet = SOCKET_ERROR; 18. / 初始化 Winsocket,所有 Winsocket 程序必须先使用 WSAStartup 进行初始化 19. WSADATA data; 20. ZeroMemory( 21. iRet = WSAStartup(MAKEWORD(2, 0), 22. ASSERT(SOCKET_ERROR != iRet); 23. / 建立服务端程序的监听套接字 24. SOCKET skListen = INVALID_SOCKET; 25. skListen = socket(AF_INET, SOCK_STREAM, 0); 26. ASSERT(INVALID_SOCKET != skListen); 27. / 初始化监听套接字地址信息 28. sockaddr_in adrServ; / 表示网络地址 29. ZeroMemory( 30. adrServ.sin_family = AF_INET; / 初始化地址格式,只能 为 AF_INET 31. adrServ.sin_port = htons(c_iPort); / 初始化端口,由于网 络字节顺序和主机字节顺序相反,所以必须使用 htons 将主机字节顺序转换成网络 字节顺序 32. adrServ.sin_addr.s_addr = INADDR_ANY; / 初始化 IP,由 于是服务器程序,所以可以将 INADDR_ANY 赋给该字段,表示任意的 IP 33. / 绑定监听套接字到本地 34. iRet = bind(skListen, (sockaddr*) 35. ASSERT(SOCKET_ERROR != iRet); 36. / 使用监听套接字进行监听 37. iRet = listen(skListen, FD_SETSIZE); / SOMAXCONN 表示可以 连接到该程序的最大连接数 38. ASSERT(SOCKET_ERROR != iRet); 39. cout 0) 59. 60. sockaddr_in adrClt; 61. int iLen = sizeof(sockaddr_in); 62. ZeroMemory( 63. SOCKET s = accept(skListen, (sockaddr*) 64. ASSERT(INVALID_SOCKET != s); 65. sl.push_back(s); 66. cout 0) 78. 79. for(SocketListIterator iter = sl.begin(); iter != sl.end(); +iter) 80. 81. / 如果有数据可读, 则遍历套接字列表中的所有套接字 82. / 检测出有数据可读的套接字 83. iRet = FD_ISSET(*iter, 84. if(iRet 0) 85. 86. / 读取套接字上的数据 87. const int c_iBufLen = 512; 88. char szBufc_iBufLen + 1 = 0; 89. int iRead = SOCKET_ERROR; 90. iRead = recv(*iter, szBuf, c_iBufLen, 0); 91. if (0 = iRead)/ 读取出现错误或者对方关闭连接 92. 93. iRead = 0 ? cout 0); 145. if (SOCKET_ERROR = iRet) 146. 147. return false; 148. 149. / 清理该套接字的资源 150. iRet = closesocket(*ps); 151. if (SOCKET_ERROR = iRet) 152. 153. return false; 154. 155. *ps = INVALID_SOCKET; 156. return true; 157. #include #include #include #include #pragma comment(lib, “ws2_32.lib“)#define ASSERT assert using std:cin;using std:cout; using std:endl;using std:list; typedef list SocketList;typedef list:iterator SocketListIterator; static const int c_iPort = 10001;bool GraceClose(SOCKET *ps); int main() int iRet = SOCKET_ERROR; / 初 始 化 Winsocket, 所 有 Winsocket程 序 必 须 先 使 服务器程序的重点是我们需要将接受自客户端程序的套接字加入到一个链表中,以方 便我们的管理。 view plaincopy to clipboardprint? 1. FD_SET(skListen, 2. iRet = select(1, 3. if(iRet 0) 4. 5. sockaddr_in adrClt; 6. int iLen = sizeof(sockaddr_in); 7. ZeroMemory( 8. SOCKET s = accept(skListen, (sockaddr*) 9. ASSERT(INVALID_SOCKET != s); 10. sl.push_back(s); 11. cout 0) sockaddr_in adrClt;int iLen = sizeof(sockaddr_ ZeroMemory(SOCKET s = accept(skListen, ASSERT(INVALID_SOCKET != s)sl.push_back(s); cout = iRead)/ 读取出现错误或者对方关闭连接 2. 3. iRead = 0 ? cout = iRead)/ 读 取 出 现 错 误 或 者 对 方 关 闭 连 接 iRe iReASS view plaincopy to clipboardprint? 1. sl.remove(INVALID_SOCKET); / 删除无效的套接字, 套接字在关闭后被设置 为无效 sl.remove(INVALID_SOCKET); / 删 除 无 效 的 套 接 字 , 套 接 接下来再让我们来看看客户端程序的代码。 view plaincopy to clipboardprint? 1. #include 2. #include 3. #include 4. #pragma comment(lib, “ws2_32.lib“) 5. #define ASSERT assert 6. using std:cin; 7. using std:cout; 8. using std:endl; 9. static const char c_szIP = “127.0.0.1“; 10. static const int c_iPort = 10001; 11. bool GraceClose(SOCKET *ps); 12. int main() 13. 14. int iRet = SOCKET_ERROR; 15. / 初始化 Winsocket,所有 Winsocket 程序必须先使用 WSAStartup 进行 初始化 16. WSADATA data; 17. ZeroMemory( 18. iRet = WSAStartup(MAKEWORD(2, 0), 19. ASSERT(SOCKET_ERROR != iRet); 20. / 建立连接套接字 21. SOCKET skClient = INVALID_SOCKET; 22. skClient = socket(AF_INET, SOCK_STREAM, 0); 23. ASSERT(INVALID_SOCKET != skClient); 24. / 初始化连接套接字地址信息 25. sockaddr_in adrServ; / 表示网络地址 26. ZeroMemory( 27. adrServ.sin_family = AF_INET; / 初始化地址格式,只能为 AF_INET 28. adrServ.sin_port = htons(c_iPort); / 初始化端口,由于网络 字节顺序和主机字节顺序相反,所以必须使用 htons 将主机字节顺序转换成网络字 节顺序 29. adrServ.sin_addr.s_addr = inet_addr(c_szIP); / 初始化 IP, 由于网 络字节顺序和主机字节顺序相反,所以必须使用 inet_addr 将主机字节顺序转换成 网络字节顺序 30. / 将套接口从阻塞状态设置到非阻塞状态 31. unsigned long ulEnable = 1; 32. iRet = ioctlsocket(skClient, FIONBIO, 33. ASSERT(SOCKET_ERROR != iRet); 34. fd_set fsWrite; 35. TIMEVAL tv; 36. tv.tv_sec = 1; 37. tv.tv_usec = 0; 38. cout szBuf; 69. if(0 = strcmp(“exit“, szBuf) 70. 71. break; 72. 73. FD_ZERO( 74. FD_SET(skClient, 75. / 如果集合 fsWrite 中的套接字有信号, 则可以用 send 操作写数据 76. iRet = sel
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度医疗设备研发与应用合同3篇
- 二零二五版私募股权投资基金股权收购合同2篇
- 二零二五版企业股权激励项目执行与改进合同2篇
- 二零二五年度房产投资分期付款合同模板3篇
- 二零二五年蔬菜种子进口合同2篇
- 二零二五年度酒楼市场拓展与股权激励方案合同2篇
- 二零二五年模具生产项目质量保证合同3篇
- 二零二五版智能家居货款担保合同范本3篇
- 二零二五年船舶抵押借款合同范本修订版3篇
- 二零二五年户外活动用安全护栏租赁合同3篇
- 2024至2030年中国柔性电路板(FPC)行业市场深度分析及发展趋势预测报告
- IGCSE考试练习册附答案
- 小学三年级下一字多义(答案)
- Unit 6 同步练习人教版2024七年级英语上册
- 农耕研学活动方案种小麦
- 九三学社申请入社人员简历表
- 非诺贝特酸胆碱缓释胶囊-临床用药解读
- 设备管理:设备管理的维护与保养
- 2024年佛山市劳动合同条例
- 土特产行业现状分析
- 苏教版五年级上册数学简便计算大全500题及答案
评论
0/150
提交评论