Socket 的 I/O调用可能会因为多种原因而阻塞:
1)数据输入方法 read()和 receive()在没有数据可读时会阻塞
2)TCP套接字的 write()方法在没有足够的空间缓存传输的数据时可能阻塞
3)ServerSocket的 accept()方法和 Socket 的构造函数都会阻塞等待,直到连接建立。
accept(),read()和 receive()
可以使用 Socket 类、ServerSocket 类和 DatagramSocket类的setSoTimeout()方法。设置其阻塞的最长时间(以毫秒为单位),如果在指定时间内这些方
法没有返回,则将抛出一个 InterruptedIOException 异常。对于 Socket 实例,在调用 read()方法前,可以使用该套接字的 InputStream的 available()方法来检测是否有可读的数据。
连接和写数据
Socket 类的构造函数会尝试根据参数中指定的主机和端口来建立连接,并阻塞等待,直到连接成功建立或发生了系统定义的超时。不幸的是,系统定义的超时时间很长,而 Java又没有提供任何缩短它的方法。要改变这种情况,可以使用 Socket 类的无参数构造函数,它返回的是一个没有建立连接的 Socket 实例。需要建立连接时,调用该实例的 connect()方法,并指定一个远程终端和超时时间(毫秒)。
write()方法调用也会阻塞等待,直到最后一个字节成功写入到了 TCP实现的本地缓存中,如果可用的缓存空间比要写入的数据小,在 write()方法调用返回前,必须把一些数据成
功传输到连接的另一端,因此,write()方法的阻塞总时间最终还是取决于接收端的应用程序。Java 现在还没有提供任何使 write()超时或由其他线程将其打断的方法,所以如果一个可以在 Socket 实例上发送大量数据的协议可能会无限期地阻塞下去。
限制每个客户端的时间
协议实例保持了对剩余服务时间的跟踪,并使用 setSoTimeout()方法来保证 read()方法的阻塞时间不会超过 TIMELIMIT,由于没有办法限制 write()调用的时间,我们并不能保证所定义的时间限制真正有效。只能在程序中自己实现,或者使用NIO
多接收者
到目前为止,我们的套接字都处理的是两个实体之间的通信,通常是一个服务器和一个客户端。这种一对一的通信方法有时称为单播(unicast)。而对于某些信息,多个接收者都可能对其感兴趣。网络提供了一个更有效地使用带宽的方法。我们可以将复制数据包的工作交给网络来做,而不是由发送者负责。
有两种类型的一对多服务:广播(broadcast)和多播(multicast)。
对于广播,(本地)网络中的所有主机都会接收到一份数据副本。对于多播,消息只是发送给一个多播地址(multicast address),网络只是将数据分发给那些表示想要接收发送到该多播地址的数据的主机。总的来说,只有 UDP 套接字允许广播或多播。
广播
广播 UDP 数据报文与单播数据报文相似,唯一的区别是其使用的是一个广播地址而不是一个常规的(单播)IP地址。