转载自:http://edu.xvna.com/html/68165.html
Socket选择可以指定Socket类发送和接受数据的方式。在JDK1.4中共有8个Socket选择可以设置。这8个选项都定义在java.net.SocketOptions接口中:
public final static int TCP_NODELAY = 0x0001 ;
public final static int SO_REUSEADDR = 0x04 ;
public final static int SO_LINGER = 0x0080 ;
public final static int SO_TIMEOUT = 0x1006 ;
public final static int SO_SNDBUF = 0x1001 ;
public final static int SO_RCVBUF = 0x1002 ;
public final static int SO_KEEPALIVE = 0x0008 ;
public final static int SO_OOBINLINE = 0x1003 ;
这8个选项除了第一个没在SO前缀外,其他7个选项都以SO作为前缀。其实这个SO就是Socket Option的缩写;因此,在Java中约定所有以SO为前缀的常量都表示Socket选项;当然,也有例外,如TCP_NODELAY。
1.TCP_NODELAY
在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到较大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法;在默认情况下,Nagle算法是开启的。
这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实现性的要求比较高的情况下(如游戏、Telnet等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle算法时就使用它,不需要时就关闭它。而使用setTcpToDelay正好可以满足这个需求。当使用setTcpNoDelay(true)将Nagle算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出去。
2.SO_REUSEADDR
这个选项可以使多个Socket对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close方法关闭Socket连接后,Socket对象所绑定的端口并不一定马上释放;系统有时在Socket连接关闭才会再确认一下是否有因为延迟面未到达的数据包,这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket类时完全不会感觉到。
这种处理机制对于随机绑定端口的Socket对象没有什么影响,但对于绑定在固定端口的Socket对象就可能会抛出“Address already in use: JVM_Bind”例外。因此,使用这个选项可以避免个例外的发生。
许多服务器程序都使用固定的端口,当服务器程序关闭后, 有可能它的端口还会被占用一段时间, 如果此时立刻在同一个主机上重启服务器程序, 由于端口已经被占用, 使得服务器程序无法绑定到该端口, 启动失败.为了确保一个进程关闭Socket 后, 即使它还没释放端口, 同一个主机上的其他进程还可以立即重用该端口, 可以调用Socket 的setResuseAddress(true) 方法。
使用SO_REUSEADDR选项时有两点需要注意:
1. 必须在调用bind方法之前使用setReuseAddress方法来打开SO_REUSEADDR选项。因此,要想使用SO_REUSEADDR选项,就不能通过Socket类的构造方法来绑定端口。
2. 必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR选项都打开才能起作用。
3.SO_LINGER
这个Socket选项可以影响close方法的行为。在默认情况下,当调用close方法后,将立即返回;如果这时仍然有未被送出的数据包,那么这些数据包将被丢弃。如果将linger参数设为一个正整数n时(n的值最大是65,535),在调用close方法后,将最多被阻塞n秒。在这n秒内,系统将尽量将未送出的数据包发送出去;如果超过了n秒,如果还有未发送的数据包,这些数据包将全部被丢弃;而close方法会立即返回。如果将linger设为0,和关闭SO_LINGER选项的作用是一样的。
如果底层的Socket实现不支持SO_LINGER都会抛出SocketException例外。当给linger参数传递负数值 时,setSoLinger还会抛出一个IllegalArgumentException例外。可以通过getSoLinger方法得到延迟关闭的时间,如果返回-1,则表明SO_LINGER是关闭的。
4.SO_TIMEOUT
这个Socket选项用来设置读取数据超时。当输入流的read方法被阻塞时,如果设置timeout(timeout的单位是毫秒),那么系统在等待了timeout毫秒后会抛出一个InterruptedIOException例外。在抛出例外后,输入流并未关闭,可以继续通过read方法读取数据。
如果将timeout设为0,就意味着read将会无限等待下去,直到服务端程序关闭这个Socket.这也是timeout的默认值。如下面的语句将读取数据超时设为30秒:socket1.setSoTimeout( 30 * 1000 );
当底层的Socket实现不支持SO_TIMEOUT选项时,这两个方法将抛出SocketException例外。不能将timeout设为负数,否则setSoTimeout方法将抛出IllegalArgumentException例外。
5.SO_SNDBUF
在默认情况下,输出流的发送缓冲区是8096个字节(8K)。这个值是Java所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用 setSendBufferSize方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。
如果底层的Socket实现不支持SO_SENDBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setSendBufferedSize方法将抛出IllegalArgumentException例外。
6.SO_RCVBUF
在默认情况下,输入流的接收缓冲区是8096个字节(8K)。这个值是Java所建议的输入缓冲区的大小。如果这个默认值不能满足要求,可以用 setReceiveBufferSize方法来重新设置缓冲区的大小。但最好不要将输入缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。
如果底层的Socket实现不支持SO_RCVBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setReceiveBufferSize方法将抛出IllegalArgumentException例外。
7.SO_KEEPALIVE
如果将这个Socket选项打开,客户端Socket每隔段的时间(大约两个小时)就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,在大约11分钟后,客户端Socket再发送一个数据包,如果在12分钟内,服务器还没响应,那么客户端Socket将关闭。如果将Socket选项关闭,客户端Socket在服务器无效的情况下可能会长时间不会关闭。SO_KEEPALIVE选项在默认情况下是关闭的,可以使用如下的语句将这个SO_KEEPALIVE选项打开:socket1.setKeepAlive( true );
8.SO_OOBINLINE
如果这个Socket选项打开,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,而是立即发出。虽然在客户端并不是使用OutputStream向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服务端程序中并不知道由客户端发过来的数据是由OutputStream还是由sendUrgentData发过来的。注意:在使用setOOBInline方法打开SO_OOBINLINE选项时要注意是必须在客户端和服务端程序同时使用setOOBInline方法打开这个选项,否则无法命名用sendUrgentData来发送数据。
补充:
服务类型选项
IP 规定了 4 种服务类型, 用来定性地描述服务的质量.
低成本: 发送成本低.
高可靠性: 保证把数据可靠地送达目的地.
最高吞吐量: 一次可以接收或发送大批量的数据.
最小延迟: 传输数据的速度快, 把数据快速送达目的地.
这 4 种服务类型还可以进行组合. 例如, 可以同时要求获得高可靠性和最小延迟.
Socket类中提供了设置和读取服务类型的方法.setTrafficClass(int trafficClass),getTrafficClass() ,用 4 个整数表示服务类型.
低成本: 0x02
高可靠性: 0x04
最高吞吐量: 0x08
最小延迟: 0x10
例如, 以下代码请求高可靠性传输服务:
socket = new Socket(host, port);
socket.setTrafficClass(0x04);
再例如, 以下代码请求高可靠性和最小延迟传输服务:
socket.setTrafficClass(0x04|0x10);//把 0x04 与 0x10 进行位或运算
设定连接时间、延迟和带宽的相对重要性
在 JDK 1.5 中, 还为 Socket 类提供了一个 setPerformancePreferences()方法:
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
以上方法的 3 个参数表示网络传输数据的 3 选指标.
connectionTime: 表示用最少时间建立连接.
latency: 表示最小延迟.
bandwidth: 表示最高带宽.
setPerformancePreferences() 方法用来设定这 3 项指标之间的相对重要性. 可以为这些参数赋予任意的整数, 这些整数之间的相对大小就决定了相应参数的相对重要性.
例如, 如果参数 connectionTime 为 2, 参数 latency 为 1, 而参数bandwidth 为 3, 就表示最高带宽最重要, 其次是最少连接时间, 最后是最小延迟.
来源:CSDN
作者:zero__007
链接:https://blog.csdn.net/zero__007/article/details/51723434