现在的位置: 首页 > 综合 > 正文

Socket注意事项

2018年04月04日 ⁄ 综合 ⁄ 共 2120字 ⁄ 字号 评论关闭

1. 堵塞Method

accept(),read()和receive() 都是堵塞的

我们可以使用Socket类、ServerSocket类和DatagramSocket类的setSoTimeout()方法,

设置其阻塞的最长时间(以毫秒为单位)。

如果在指定时间内这些方法没有返回,则将抛出一个InterruptedIOException异常

对于Socket实例,在调用read()方法前,我们还可以使用该Socket的InputStream.available()方法来检测是否有可读的数据。

超时设置为0表示该操作永不超时。如果阻塞超过了超时时长,则抛出一个异常。InterruptIOException

2.连接超时 

Socket socket = new Socket(server, servPort);
会尝试根据参数中指定的主机和端口来建立连接,并阻塞等待,直到连接成功建立或发生了系统定义的超时。
系统定义的超时时间很长,而Java又没有提供任何缩短它的方法。
解决:
Socket socket =  new Socket();//创建一个无连接的Socket
socket.connect(soctetAdress,timeout);

3.保持活跃(keep-alive) 

如果一段时间内没有数据交换,通信的每个终端可能都会怀疑对方是否还处于活跃状态。

TCP协议提供了一种keep-alive的机制,该机制在经过一段不活动时间后,将向另一个终端发送一个探测消息。

如果另一个终端还出于活跃状态,它将回复一个确认消息。

如果经过几次尝试后依然没有收到另一终端的确认消息,则终止发送探测信息,关闭套接字,并在下一次尝试I/O操作时抛出一个异常。

注意,应用程序只要在探测信息失败时才能察觉到keep-alive机制的工作。

默认情况下,keep-alive机制是关闭的。通过调用socket.setKeepAlive(true)来开启keep-alive机制。

4. 缓存区 

一旦创建了一个Socket或DatagramSocket实例,操作系统就必须为其分配缓存区以存放接收的和要发送的数据。

Socket, DatagramSocket: 设置和获取发送接收缓存区大小
        int getReceiveBufferSize() 
        void setReceiveBufferSize(int size) 
        int getSendBufferSize() 
        void setSendBufferSize(int size)
这里指定的大小只是作为一种建议给出的,实际大小可能与之存在差异。

ServerSocket: 设置/获取所接受套接字的接收缓冲区大小
    int getReceiveBufferSize() 
    void setReceiveBufferSize(int size)
实际上用于获取和设置由accept()方法创建的Socket实例的接收缓冲区的大小(字节)。

5. 消除缓冲延迟

TCP协议将数据缓存起来直到足够多时一次发送,以避免发送过小的数据包而浪费网络资源。
虽然这个功能有利于网络,但有些应用程序可能对所造成的缓冲延迟不能容忍
socket.getTcpNoDelay()和socket.setTcpNoDelay()方法用于获取和设置是否消除缓冲延迟。

将值设置为true表示禁用缓冲延迟功能。

6.关闭后停留

socket.setSoLinger(true),那么后面再调用的close()方法将阻塞等待,
直到远程终端对所有数据都返回了确认信息,或者发生了指定的超时(秒)。
如果发生了超时,TCP连接将强行关闭。

如果开启了停留功能,getSoLinger()方法将返回指定的超时时间,否则返回-1。

7.深入剖析

a.缓冲和TCP

作为程序员,在使用TCP套接字时需要记住的最重要一点是:
不能假设在连接的一端将数据写入输出流和在另一端从输入流读出数据之间有任何一致性。
尤其是在发送端由单个输出流的write()方法传输的数据,可能会通过另一端的多个输入流的read()方法来获取;
而一个read()方法可能会返回多个write()方法传输的数据。

TCP发送接受数据时底层的三个FIFO队列:
1. SendQ:在发送端底层实现中缓存的字节,这些字节已经写入输出流,但还没在接收端主机上成功接收。
2. RecvQ:在接收端底层实现中缓存的字节,等待分配到接收程序--即从输入流中读取。
3. Delivered:接收者从输入流已经读取到的字节。
调用out.write()方法将向SendQ追加字节。TCP协议负责将字节按顺序从SendQ移动到RecvQ
接收程序从Socket的InputStream读取数据时,字节就从RecvQ移动到Delivered

b.死锁风险

解决:读,写分别用不同的线程
 

c.TCP生命周期

3次握手: 
一条从客户端到服务器端的连接请求,
一条从服务器端到客户端的确认消息,
一条从客户端到服务器端的确认消息。

客户端一收到服务器端发来的确认消息,就立即认为连接已经成功建立。

 

【上篇】
【下篇】

抱歉!评论已关闭.