本文共 3195 字,大约阅读时间需要 10 分钟。
阻塞模式和非阻塞模式
网络不是一个稳定可靠的,存在各种异常情况,比如connect和服务端
三次握手失败,那这个函数就会阻塞,各种问题,可以设置非阻塞,
超时处理,1可以用Socket进行设置,但是考虑到跨平台可能有些函数用不到, 就只能用一种,select多路复用,把socket变成非阻塞模式,
第一步就是把socket设置成非阻塞模式,
1Windows设置阻塞和非阻塞
1 | bool setBlock( bool isblock); |
有得时候需要阻塞模式,有得时候不需要阻塞模式.主要的目的是
实现超时的控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool XTCP::SetBlock( bool isblock) { if (m_sock <= 0) return false ; unsigned long ul = 0; if (!isblock) { //connect 会立刻返回 ul = 1; } // FIONBIO 阻塞模式 //ul =0 表示阻塞模式 //设置模式 ioctlsocket(m_sock, FIONBIO, &ul); return true ; } |
测试代码, 发现了就是设置false的时候,connect经过的非常快
一下就返回了,但是设置称true的时候,connect就是阻塞的模式会阻塞
几秒钟.
1 2 3 4 5 6 7 8 9 10 | #include "XTCP.h" int main( int argc, char *argv[]) { XTCP client; client.CreateSocket(); client.SetBlock( false ); client.Connect( "192.168.1.125" ,846); getchar (); return 0; } |
2Linux设置阻塞和非阻塞
Windows和Linux的接口函数又不一样了,
头文件 #include <fcntl.h>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | bool XTCP::SetBlock( bool isblock) { if (m_sock <= 0) return false ; unsigned long ul = 0; if (!isblock) { //connect 会立刻返回 ul = 1; } // FIONBIO 阻塞模式 //ul =0 表示阻塞模式 #ifdef WIN32 ioctlsocket(m_sock, FIONBIO, &ul); #else //操作文件描述法,先获取属性 int flags = fcntl(m_sock,F_GETFL,0); if (flags < 0) return false ; //出错了 if (isblock) { //阻塞模式 flags = flags &~ O_NONBLOCK; } else { //非阻塞模式 flags = flags | O_NONBLOCK; } if (fcntl(m_sock, F_SETFL, flags)!=0) return false ; #endif return true ; } |
不过Linux和windows有写不同,是连接的ip存在,但端口不存在
他会立刻反回,所以测试的时候需要把ip改成不存的的测试下.
测试的时候就会发现,false会立刻反回,
他是一个多路复用,不管连接是否成功,放在后端通过select读取
连接信息,这样的话,非阻塞就设置成功
3通过select实现connect跨平台超时处理
重启connect函数,默认我们是1秒钟,
1 | bool Connect( const char *ip, unsigned short port, int timeoutms=1000); |
定义connect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | bool XTCP::Connect( const char *ip, unsigned short port, int timeoutms) { //如果socket没有创建 if (m_sock <= 0) CreateSocket(); //连接需要这个结构体 sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); //本地字节序转网络字节序 saddr.sin_addr.s_addr = inet_addr(ip); //改成非阻塞模式 SetBlock( false ); //文件句柄数组 存放文件句柄当前的状态 fd_set set; //这里会立即反回 if (connect(m_sock, (sockaddr*)&saddr, sizeof (saddr)) != 0) { //给文件句柄数据 置空 FD_ZERO(&set); //把socket加到这里面 FD_SET(m_sock, &set); //通过select监听这个文件序列是否有可读或可写 //第一个参数就是监听的所有文件句柄最大值+1 早windows中可有可无,在linu必须设置 //select是可以监听多个文件句柄的 linux中就是文件描述符 //第二个参数是监听的可读的序列 先用不到 //第三个参数是可写的 set是一组文件描述符列表,只要有其中一个可写, //select函数就返回 如果没有select本身就是阻塞的 如果 //第四个是一个错误处理 //第五个设置超时时间 是一个结构体 //如果成功反回文件描述符的值 如果失败返回-1 ..超时返回0 timeval tm ; tm .tv_sec = 0; //秒 tm .tv_usec = timeoutms*1000; //微秒 if (select(m_sock + 1, 0, &set, 0, & tm ) <= 0) { //失败或超时 printf ( "connect timeout or error!\n" ); return false ; } } //恢复阻塞模式 SetBlock( true ); printf ( "connect %s:%d success!\n" , ip, port); return true ; } |
在linux编译测试,
测试代码
执行这个客户端的时候,我们默数3秒,然后accent就反回了,
也是是阻塞超时实现. 3000毫秒也就是3秒 一般情况下500毫秒
连接不是就是网络出问题了,
1 2 3 4 5 6 7 8 9 | #include "XTCP.h" int main( int argc, char *argv[]) { XTCP client; client.CreateSocket(); client.Connect( "192.168.1.13" ,846,3000); getchar (); return 0; } |
效果还是很不错的.
本文转自超级极客51CTO博客,原文链接:http://blog.51cto.com/12158490/1948229,如需转载请自行联系原作者