控制connect超时时间(linux版本和Windows版本)
来源:互联网 发布:matlab遗传算法cae 编辑:程序博客网 时间:2024/05/29 15:59
客户端在连接服务器时,可能会出现问题,导致三次握手无法完成,持续重试,表现在客户端程序的行为就是卡在connect调用上无法返回,这样的客户端是非常不友好的。
大致的原理就是设置socket为非阻塞,这是connect会马上返回,之后通过select控制超时,并通过FD_ISSET()检测,再通过getsockopt()检测SO_ERROR,最后再把socket设置为阻塞模式。之后就可以愉快地通讯了。
linux正确程序:
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <time.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { int fd, retval; struct sockaddr_in addr; struct timeval timeo = {3,0}; socklen_t len = sizeof(timeo); fd_set set; fd = socket(AF_INET, SOCK_STREAM, 0); if (argc == 4) timeo.tv_sec = atoi(argv[3]); int savefl = fcntl(fd,F_GETFL); fcntl(fd, F_SETFL, savefl | O_NONBLOCK); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); addr.sin_port = htons(atoi(argv[2])); printf( "%d\n ", time(NULL)); if (connect(fd, (struct sockaddr*)&addr, sizeof(addr))==0) { close(fd); printf( "connected..1\n "); return 0; } if (errno != EINPROGRESS){ close(fd); perror( "connect..2 "); return -1; } FD_ZERO(&set); FD_SET(fd, &set); retval = select(fd + 1, NULL, &set, NULL, &timeo); if (retval == -1) { close(fd); perror( "select "); return -1; } else if(retval == 0) { close(fd); fprintf(stderr, "timeout\n "); printf( "%d\n ", time(NULL)); return 0; } if(FD_ISSET (fd,&set)) { int error = 0; socklen_t len = sizeof (error); if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { printf ( "getsockopt fail,connected fail\n "); return -1; } if (error == ETIMEDOUT) { printf ( "connected timeout\n "); } if(error == ECONNREFUSED) { printf( "No one listening on the remote address.\n "); return -1; } } printf ( "connected .. 3\n "); fcntl(fd, F_SETFL, savefl); close (fd); return 0; }
注意:
- 一定要用getsockopt检测SO_ERROR,这个检测在man connect中被指定了要检测
- EINPROGRESS The socket is non-blocking and the connection cannot be
completed immediately. It is possible to select(2) or poll(2) for
completion by selecting the socket for writing. After select(2)
indicates writability, use getsockopt(2) to read the SO_ERROR option
at level SOL_SOCKET to determine whether connect() completed
successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of
the usual error codes listed here, explaining the reason for the
failure).
windows下的程序类似,只是换了相应的函数。
windows代码如下:
INT InitTcpSocket(LPCTSTR ip, INT port){ SOCKET sock; struct sockaddr_in serveraddr; struct timeval timeo = {3, 0}; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(port); serveraddr.sin_addr.s_addr = inet_addr(ip); if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET ) return INVALID_SOCKET; u_long arg = 1; if(ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) { LogUtil::Logger(LOG_DEBUG,"ioctlsocket 设置失败"); return INVALID_SOCKET; } CString esg("测试网络"); LogUtil::Logger(LOG_DEBUG,esg); int i=0; if ((i=connect(sock,(struct sockaddr *)&serveraddr, sizeof(serveraddr)))== 0) { LogUtil::Logger(LOG_DEBUG,"connect successful!"); arg = 0; ioctlsocket(sock, FIONBIO, &arg); return sock; } int nErrCode = WSAGetLastError(); esg.Format("%s%d,%s%d","connect return:",i,"nError:",nErrCode); LogUtil::Logger(LOG_DEBUG,esg); if (nErrCode != WSAEWOULDBLOCK) { closesocket(sock); esg.Format("%s%d","connect failed!",errno); LogUtil::Logger(LOG_DEBUG,esg); return INVALID_SOCKET; } FD_ZERO(&r); FD_SET(sock, &r); int rev=select(sock+1, 0, &r, 0, &timeo); //需要注意select函数第一个参数在winsock被忽略了,在linux必须是sock+1; if( rev == 0) { LogUtil::Logger(LOG_DEBUG,"select timeout!"); closesocket(sock); return INVALID_SOCKET; } if (rev ==-1) { LogUtil::Logger(LOG_DEBUG,"select error!"); closesocket(sock); return INVALID_SOCKET; } if(FD_ISSET(sock,&r)) { int error = 0; int len = sizeof (error); LogUtil::Logger(LOG_DEBUG,"FD_ISSET"); if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &len) < 0) { LogUtil::Logger(LOG_DEBUG,"getsockopt fail,connected fail!"); return INVALID_SOCKET; } if (error == WSAETIMEDOUT) { LogUtil::Logger(LOG_DEBUG,"connected timeout!"); return INVALID_SOCKET; } if(error == WSAECONNREFUSED) { LogUtil::Logger(LOG_DEBUG,"No one listening on the remote address!"); return INVALID_SOCKET; } } arg = 0; ioctlsocket(sock, FIONBIO, &arg); return sock;}
0 0
- 控制connect超时时间(linux版本和Windows版本)
- 带超时时间的telnet该怎么玩?------检测tcp是否可连接时经常用到(本文仅给出linux版本,之前博客也有Windows版本的)
- HttpClient 4.5版本设置连接超时时间
- HttpClient 4.5版本设置连接超时时间
- HttpClient 4.5版本设置连接超时时间
- HttpClient 4.5版本设置连接超时时间
- HttpClient 4.5版本设置连接超时时间
- HttpClient 4.5版本设置连接超时时间
- HttpClient不同版本超时时间的设置
- git 删除版本控制(文件夹)windows
- 版本控制工具Git(Windows端)
- Linux 的版本控制
- Linux 的版本控制
- Linux 的版本控制
- Linux 的版本控制
- Windows和Linux双系统修改默认启动项、超时时间
- 设置connect超时时间
- 设置connect超时时间
- Linux 通过Remi 源 安装mysql php
- 以DxR算法思想为基准设计出的路由项定位结构图解
- 成发过火可的规划局的法规和
- 【Android基础篇】SimpleAdapter重写时响应CheckBox复选框状态改变
- 虚拟主机++iRedMail搭建邮箱服务器
- 控制connect超时时间(linux版本和Windows版本)
- Sicily 13915. Sequence
- 九度OJ-1019-简单计算器
- Java中equals方法解析
- Belated enlightenment on Quaternion
- 自然语言处理(NLP)网上资源整理
- Qt数据库总结
- Spring3工具类
- sorting - insert sort