参考代码:
SocketAddress address = new InetSocketAddress("192.168.0.104", 8888); ServerSocket s = new ServerSocket(); s.bind(address); Socket socket = s.accept(); socket.setSoTimeout(5 * 1000); //5秒钟
将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。
如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。
另外:超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。
JAVA SOCKET编程中 SOCKET中connect方法是可以设置连接超时时间的,如下:
java.net.Socket
public void connect(SocketAddress endpoint, int timeout) throws IOException
注:timeout为0表示不限超时 connect调用会一直阻塞直到连接建立或发生错误; 如果timeout>0 连接在timeout毫秒内没有建立,会返回抛出SocketTimeoutException异常。
而在Linux系统编程中 connect 系统调用是不能够设置超时时间的,API如下:
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
注:sockfd 是阻塞的
从TCP协议栈的角度考虑,连接建立需要三次握手,只要没有收到应答报文,那么就会重新发送(发送的时机需要深入Linux时钟机制);如果连接最终无法建立,那么TCP最终会放弃 connect 调用;对于基于 Berkeley BSD的系统,默认时间是 75秒; 这个时间对于应用来说太长了。
既然 Linux 没有提供一种控制 TCP协议栈 connect超时时间的 API, 那么该如何设置 connect 超时呢?
1、设置 fd 非阻塞, connect 调用立即返回,错误号为 EINPROGRESS
2、注册 fd 写事件 到 select 调用,同时设置超时时间为 timeout
查找 JDK 源码,看看 JAVA 是如何实现的?
相关文件: openjdk\jdk\src\share\transport\socket\socketTransport.c
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;
}
实现思路是一致的。