JAVA SOCKET编程中 SOCKET中connect方法是能够设置链接超时时间的,以下: java
java.net.Socket public void connect(SocketAddress endpoint, int timeout) throws IOException
注:timeout为0表示不限超时 connect调用会一直阻塞直到链接创建或发生错误; 若是timeout>0 链接在timeout毫秒内没有创建,会返回抛出SocketTimeoutException异常。 编程
而在Linux系统编程中 connect 系统调用是不可以设置超时时间的,API以下: socket
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
注:sockfd 是阻塞的 .net
从TCP协议栈的角度考虑,链接创建须要三次握手,只要没有收到应答报文,那么就会从新发送(发送的时机须要深刻Linux时钟机制);若是链接最终没法创建,那么TCP最终会放弃 connect 调用;对于基于 Berkeley BSD的系统,默认时间是 75秒; 这个时间对于应用来讲太长了。 code
既然 Linux 没有提供一种控制 TCP协议栈 connect超时时间的 API, 那么该如何设置 connect 超时呢? 事件
一、设置 fd 非阻塞, connect 调用当即返回,错误号为 EINPROGRESS 源码
二、注册 fd 写事件 到 select 调用,同时设置超时时间为 timeout it
查找 JDK 源码,看看 JAVA 是如何实现的? io
相关文件: openjdk\jdk\src\share\transport\socket\socketTransport.c event
openjdk\jdk\src\solaris\transport\socket\socket_md.c
/* * To do a timed connect we make the socket non-blocking * and poll with a timeout; */ if (attachTimeout > 0) { dbgsysConfigureBlocking(socketFD, JNI_FALSE); } err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa)); if (err == DBG_EINPROGRESS && attachTimeout > 0) { err = dbgsysFinishConnect(socketFD, (long)attachTimeout); if (err == DBG_ETIMEOUT) { dbgsysConfigureBlocking(socketFD, JNI_TRUE); RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "connect timed out"); } }
int dbgsysFinishConnect(int fd, long timeout) { int rv = dbgsysPoll(fd, 0, 1, timeout); if (rv == 0) { return DBG_ETIMEOUT; } if (rv > 0) { return 0; } return rv; }
int dbgsysPoll(int fd, jboolean rd, jboolean wr, long timeout) { struct pollfd fds[1]; int rv; fds[0].fd = fd; fds[0].events = 0; if (rd) { fds[0].events |= POLLIN; } if (wr) { fds[0].events |= POLLOUT; } fds[0].revents = 0; rv = poll(&fds[0], 1, timeout); if (rv >= 0) { rv = 0; if (fds[0].revents & POLLIN) { rv |= DBG_POLLIN; } if (fds[0].revents & POLLOUT) { rv |= DBG_POLLOUT; } } return rv; }实现思路是一致的。