java Socket(阻塞) api记录点
参考资料:
《Java TCP/IP Socket 编程》 2ed
记录点:
1 accept(),read()和receive()
对于这些方法,我们可以使用Socket类、ServerSocket类和DatagramSocket类的setSoTimeout()方法,设置其阻塞的最长时间(以毫秒为单位)。如果在指定时间内这些方法没有返回,则将抛出一个InterruptedIOException异常。对于Socket实例,在调用read()方法前,我们还可以使用该套接字的InputStream的available()方法来检测是否有可读的数据.
2 一般在建立Socket的时候,如下: 由于默认的到参数的构造器时阻塞的,并且系统的超时时间定义得比较长.
Socket socket = new Socket();
try{
socket.connect(SocketAddress endpoint,int timeout);
}catch(SocketTimeOutException e){
... do with timeOut...
}catch(OtherException){
... do with other Exception...
}
3 write方法
write()方法调用也会阻塞等待,直到最后一个字节成功写入到了TCP实现的本地缓存中。如果可用的缓存空间比要写入的数据小,在write()方法调用返回前,必须把一些数据成功传输到连接的另一端。因此,write()方法的阻塞总时间最终还是取决于接收端的应用程序。不幸的是Java现在还没有提供任何使write()超时或由其他线程将其打断的方法。所以如果一个可以在Socket实例上发送大量数据的协议可能会无限期地阻塞下去。
4 使用setSoTimeout()方法粗略的实现为每个Socket客户端提供有限的服务时间.之所以说是"粗略",是因为我们没有办法控制:write()方法. 服务器端的线程方法大体如下:
long endTime = System.currentTimeMillis() + timelimit; int timeBoundMillis = timelimit; clntSock.setSoTimeout(timeBoundMillis); wile((timeBoundMillis > 0) &&((recvMsgSize = in.read(echoBuffer)) != -1)) { out.write(echoBuffer, 0, recvMsgSize); totalBytesEchoed += recvMsgSize; timeBoundMillis = (int) (endTime - System.currentTimeMillis()) ; clntSock.setSoTimeout(timeBoundMillis); //每一次的读取或者输出操作后.重新设置timelimit } //注意处理超时的异常:
5 默认的Keep-Alive 机制:
如果一段时间内没有数据交换,通信的每个终端可能都会怀疑对方是否还处于活跃状态。TCP协议提供了一种keep-alive的机制,该机制在经过一段不活动时间后,将向另一个终端发送一个探测消息。如果另一个终端还出于活跃状态,它将回复一个确认消息。如果经过几次尝试后依然没有收到另一终端的确认消息,则终止发送探测信息,关闭套接字,并在下一次尝试I/O操作时抛出一个异常。注意,应用程序只要在探测信息失败时才能察觉到keep-alive机制的工作。
6 发送和接收缓存区的大小:
一旦创建了一个Socket或DatagramSocket实例,操作系统就必须为其分配缓存区以存放接收的和要发送的数据:
int getReceiveBufferSize()
void setReceiveBufferSize(int size)
int getSendBufferSize()
void setSendBufferSize(int size)
为ServerSocket调用这些方法的时候,相当于为其accept()方法接收的Socket分配缓冲大小;
7 地址重用:
对于TCP,当一个连接关闭后,通信的一端(或两端)必须在"Time-Wait"状态上等待一段时间,以对传输途中丢失的数据包进行清理(见第6.4.2节)。不幸的是,通信终端可能无法等到Time-Wait结束。对于这两种情况,都需要能够与正在使用的地址进行绑定的能力,这就要求实现地址重用。
boolean getReuseAddress()
void setReuseAddress(boolean on)
8 消除缓冲延迟:
TCP协议将数据缓存起来直到足够多时一次发送,以避免发送过小的数据包而浪费网络资源。虽然这个功能有利于网络,但应用程序可能对所造成的缓冲延迟不能容忍。好在可以人为禁用缓存功能:
boolean getTcpNoDelay()
void setTcpNoDelay(boolean on)
9 紧急数据:
void sendUrgentData(int data)
boolean getOOBInline()
void setOOBInline(boolean on)
要发送紧急数据需要调用sendUrgentData() 方法,它将发送其int参数的最低位字节。要接收这个字节,必须为setOOBInline()方法传递true参数启用接收者对频道外数据的接收。该字节在接收者的输入流中被接收。发送于紧急字节之前的数据将处于接收者的输入流中的紧急字节前面。如果没有启用接收者接收频道外数据的功能,紧急字节将被无声地丢弃。
注意Java中的紧急数据几乎没什么用,因为紧急字节与常规字节按照传输的顺序混在了一起。实际上,Java接收者并不能区分其是否在接收紧急数据
10 关闭后停留:
当调用套接字的close()方法后,即使套接字的缓冲区中还有没有发送的数据,它也将立即返回。这样不发送完所有数据可能导致的问题是主机将在后面的某个时刻发生故障。其实可以选择让close()方法"停留"或阻塞一段时间,直到所有数据都已经发送并确认,或发生了超时.
int getSoLinger()
void setSoLinger(boolean on, int linger)
如果发生了超时,TCP连接将强行关闭
11 基于性能的协议选择:
void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
12 关闭连接:
调用Socket的close()方法将同时终止两个方向(输入和输出)的数据流.
Socket类的shutdownInput()和shutdownOutput()方法能够将输入输出流相互独立地关闭。调用shutdownInput()后,套接字的输入流将无法使用。任何没有发送的数据都将毫无提示地被丢弃,任何想从套接字的输入流读取数据的操作都将返回-1。当Socket调用shutdownOutput() 方法后,套接字的输出流将无法再发送数据,任何尝试向输出流写数据的操作都将抛出一个IOException异常。在调用shutdownOutput()之前写出的数据可能能够被远程套接字读取,之后,在远程套接字输入流上的读操作将返回-1。应用程序调用shutdownOutput()后还能继续从套接字读取数据,类似的,在调用shutdownInput()后也能够继续写数据。