Socket有以下几个选项:
TCP_NODELAY | 表示立即发送数据。 |
SO_RESUSEADDR | 表示是否允许重用Socket所绑定的本地地址。 |
SO_TIMEOUT | 表示接受数据时的等待超时时间。 |
SO_LINGER | 表示当执行Socket的close()方法时,是否立即关闭底层的Socket。 |
SO_SNFBUF | 表示发送数据的缓冲区的大小。 |
SO_RCVBUF | 表示接受数据的缓冲区的大小。 |
SO_KEEPALIVE | 表示对于长时间处于空闲状态的Socket,是否要自动把他关闭。 |
OOBINLINE | 表示是否支持发送一个字节的TCP紧急数据。 |
2.TCP_NODELAY选项:
//设置该选项: public void setTcpNoDelay(boolean on) throws SocketException //底层实现不支持,则抛出异常。 //读取该选项: public boolean getTcpNoDelay() throws SocketException
默认情况下,发送数据采用Negale算法。Negale算法是指发送方发送的数据不会立刻发出,而是先放在缓冲区内,等待缓冲区满了在发出。 发送完一批数据后,会等待接收方对这批数据的回应,然后在发送下一批数据。(适用于发送方需要发送大批量数据,并且接收方会及时作出回应的场合,而如果持续发送小批量数据,且接收方不一定会看立即发送响应数据,则使发送方会运行很慢。)
TCP_NODELAY的默认值为false,表示使用Negale算法, 若 setTcpNoDelay(true)方法,就会关闭Socket的缓冲,让数据及时发送。
if( !socket.getTcpNoDelay() ){ socket.setTcpNoDelay(true); }
3.SO_RESUSEADDR选项:
//设置该选项: public void setResuseAddress(boolean on) throws SocektException //读取该选项: public boolean getResuseAddress() throws SocketException
当接收方通过Socket的colse()方法关闭Socket时,如果网络上还有发送到这个Socket的数据,那么底层的Socket不会立刻释放而本地端口,而是会等待一段时间,确保接收到了网络上发送过来的延迟数据,然后再释放端口。但接收到延迟数据后,不会对这些数据做任何处理,只是确保这些数据不会被其他碰巧绑定到同样端口的新进程接收到。
而当服务器程序关闭后,有可能它的端口还会被占用一段时间,如果此时立刻在同一个主机上重启服务器程序,由于端口已经被占用,使得服务器程序无法绑定到该端口,启动失败。
为使同一个主机上的其他进程还可以立刻重用该端口,可以调用Socket的setReuseAddress(true);
if( !socket.getReuseAddress() ){ socket.setReuseAddress(true); }
但值得注意的是 socket.setReuseAddress(true)方法必须在Socket还没有绑定到一个本地端口之前调用,否则执行该方法就无效。因此必须按下方式创建Socket对象,在连接远程服务器。
Socket socket = new Socket(); //此时Socket对象未绑定本地端口,并且未连接远程服务器 socket.setReuseAddress(true); // 会抛出SocketException // 如果不写以下这句话,则客户程序一般采用随机端口,那么救护绑定匿名的本地端口。 SocketAddress localAddr = new InetSocketAddress("localhost",9999); SocketAddress remoteAddr = new InetSocketAddress("remotehost",8888); socket.bind(localAddr); //与本地端口绑定 socket.connect(remoteAddr); // 连接远程服务器 //两个共用同一个端口的进程必须都调用该方法才能使得一个进程关闭Socket后,另一个进程的Socket能够立刻重用相同的端口。
4.SO_TIMEOUT选项:
//设置该选项: public void setSoTimeout(int timeout) throws SocketException //读取该选项: public int getSoTimeOut() throws SocketException
当通过Socket的输入流读数据时,如果还没有数据,就会等待。例如:
byte[] buff = new byte[1024]; InputStream in = socket.getInputStream(); in.read(buff);
对于上述代码,如果输入流中没有数据,in.read(buff)就会等待对方发送数据,知道满足以下情况才结束等待:
a. 输入流中有1024个字节,read()方法吧这些字节读入到buff中,在返回读取的字节数。
b. 当寂静块接近输入流的末尾,距离末尾还有小于1024个字节时,read()方法会把这些字节读入到buff中,在返回读取的字节数。
c. 已经读到输入流的末尾,正常结束,返回-1.
d。 连接已经断开,抛出IOException。
e. 如果通过Socket的setSoTimeout()方法设置了等待超时时间,单位为毫秒,超过该时间后就抛出SocketTimeoutException。 它的默认值为0,表示会无限等待,永远不会超时。
public class ReceiveServer { public static void main(String[] args) throws IOException,SocketException{ ServerSocket server = new ServerSocket(9999); Socket s = server.accept(); s.setSoTimeout(1000*20); //设置超时时间为5秒 InputStream in = s.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int length = -1; do{ try { length = in.read(buff); if( length != -1 ){ buffer.write( buff,0,length ); } } catch (SocketTimeoutException e) { e.printStackTrace(); System.out.println("等待超时..."); } }while( length != -1 ); System.out.println( new String( buffer.toString() )); // 将字节数组转换为字符串 } } //////////////////////// public class SendClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost",9999); OutputStream out = socket.getOutputStream(); out.write( "Hello ".getBytes() ); out.write("EveryOne".getBytes() ); Thread.sleep(1000*60); socket.close(); } }
原文:http://my.oschina.net/gently/blog/531117