Java Socket 参数详解

ServerSocket有以下3个选项。

SO_TIMEOUT

1、SO_TIMEOUT:表示等待客户连接的超时时间。单位为毫秒,默认值为0 , 表示永远等待。
SO_TIMEOUT表示ServerSocket的accept()方法等待客户连接的超时时间,以毫秒为单位。如果SO_TIMEOUT的值为0,表示永远不会超时,这是SO_TIMEOUT的默认值。
当服务器执行ServerSocket的accept()方法时,如果连接请求队列为空,服务器就会一直等待,直到接收到了客户连接才从accept()方法返回。如果设定了超时时间,那么当服务器等待的时间超过了超时时间,就会抛出SocketTimeoutException,它是InterruptedException的子类。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TimeoutTest {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8000);
serverSocket.setSoTimeout(6000); //等待客户连接的时间不超过6秒
Socket socket = serverSocket.accept();
socket.close();
System.out.println("服务器关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}

当底层的Socket实现不支持SO_TIMEOUT选项时,这两个方法将抛出SocketException例外。不能将timeout设为负数,否则setSoTimeout方法将抛出IllegalArgumentException例外。

SO_REUSEADDR

2、SO_REUSEADDR:表示是否允许重用服务器所綁定的地址。
该选项用来决定如果网络上仍然有数据向旧的ServerSocket传输数据,是否允许新的ServerSocket绑定到与旧的ServerSocket同样的端口上,该选项的默认值与操作系统有关,在某些操作系统中,允许重用端口,而在某些系统中不允许重用端口。
当ServerSocket关闭时,如果网络上还有发送到这个serversocket上的数据,这个ServerSocket不会立即释放本地端口,而是等待一段时间,确保接收到了网络上发送过来的延迟数据,然后再释放端口
值得注意的是,public void setReuseAddress(boolean on) throws SocketException必须在ServerSocket还没有绑定到一个本地端口之前使用,否则执行该方法无效。此外,两个公用同一个端口的进程必须都调用serverSocket.setReuseAddress(true)方法,才能使得一个进程关闭ServerSocket之后,另一个进程的ServerSocket还能够立刻重用相同的端口

SO_RCVBUF

3、SO_RCVBUF:表示接收数据的缓冲区的大小。
表示接收数据的缓冲区的大小,无论在ServerSocket绑定本地端口之前还是之后,调用setReceiveBufferSize()方法都有效。示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RecvBufferSizeTest {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8000);
int size = serverSocket.getReceiveBufferSize();
System.out.println("修改前的缓冲大小:" + size);
if(size < 130172) {
serverSocket.setReceiveBufferSize(130172); //把缓冲区的大小设置为128KB
}
System.out.println("修改后的缓冲大小:" + serverSocket.getReceiveBufferSize());
}catch (IOException e) {
e.printStackTrace();
}
}
}

SO_BACKLOG

4、SO_BACKLOG 输入连接指示(对连接的请求)的最大队列长度被设置为backlog参数。如果队列满时收到连接指示,则拒绝该连接。
4.1. backlog参数必须是大于 0 的正值。如果传递的值等于或小于 0,则假定为默认值。
4.2. 经过测试这个队列是按照FIFO(先进先出)的原则。
4.3. 如果将accept这个函数放在一个循环体中时,backlog参数也不会有什么作用。或者简单的讲运行ServerSocket的这个线程会阻塞时,无论是在accept,还是在read处阻塞,这个backlog参数才生效。
4.4.建一个ServerSocket实例,绑定到端口10000,backlog设置为2

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
public class BackLogTest {
private static Logger logger = Logger.getLogger(Test_backlog.class);
public static void main(String[] args) throws Exception {
BufferedReader in = null;
PrintWriter out = null;
int backlog = 2;
ServerSocket serversocket = new ServerSocket(10000, backlog);
while (true) {
logger.debug("启动服务端......");
int i;
Socket socket = serversocket.accept();
logger.debug("有客户端连上服务端, 客户端信息如下:" + socket.getInetAddress() + " : " + socket.getPort() + ".");
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
do {
char[] c = new char[1024];
i = in.read(c);
logger.debug("服务端收到信息: " + new String(c, 0, i));
} while (i == -1);
out.close();
in.close();
socket.close();
logger.debug("关闭服务端......");
}
}
}

当连接数大于2的时候不会有任何输出。