服务器端用于创建TCP连接的通道,只能对accept事件感兴趣。accept方法会返回一个已和客户端连接好的SocketChannel通道,它才服务器是真正传输数据的通道。
2.3 SocketChannel
TCP客户端和TCP服务器端都用它来传输数据。
客户端必须调用connect方法去连接服务器。在非阻塞通模式中,该方法将当前通道加入到选择器的已注册集合中,然后通过异步方式进行创建TCP连接,然后该方法立刻返回。注意调用该方法后并不表示已经创建好了TCP连接,如果这个方法返回false,稍后必须调用finishConnect方法来完成客户端到服务器的tcp连接。在阻塞方式中,connect方法会阻塞直到创建好了TCP连接。
finishConnect在非阻塞模式中仅仅是返回连接的状态。返回true时,表示连接创建好了。在阻塞模式下,直接调用方法connect即可完成连接,不需要使用finishConnect。
非阻塞模式��,读写操作要配合选择器一起使用。在阻塞模式下,创建好TCP连接后就可以直接对通道进行读写操作。
2.4 DatagramChannel
connect方法仅用于客户端到服务器端的连接,连接的作用仅仅是避免每次发送和接受数据时的安全检查,提高发送和接受数据的效率,而不是像TCP连接那样表示握手的意思。客户端通道只有调用了connect方法后,才能使用read和write方法读写数据。
客户端也可以不事先调用connet方法,而直接使用receive方法和send方法来实现数据的收发。
abstract SocketAddress
receive(ByteBuffer dst)
abstract int
send(ByteBuffer src, SocketAddress target)
2.5 服务器端DatagramChannel和SocketChannel的区别
对于服务器端DatagramChannel(UDP)和SocketChannel(TCP)有明显的区别,对于TCP连接,服务器端每创建一个连接就对应一个通道(不同的客户端ip:port地址对应一个通道),而服务器端UDP的连接始终只有一个通道,所有客户端发送过来的报文都存放于同一个缓冲区中,这显然会降低服务器端的效率,好在DatagramChannel对象是线程安全的,可以用多个线程读写同一个UDP通道。
服务器端为什么只有一个通道呢?我猜想因为UDP是无状态的,不知道什么时客户端会发送数据,什么时候数据又发送完成,所以服务器端没有办法为每个客户端创建一个通道,就算服务器端根据客户端ip:port为每个客户端创建了通道,服务器端也不知道什么时候该释放这个通道,这就造成了资源的浪费。
4. Selector
Selector类表示选择器,通过这个类的对象可以选取已就绪的通道和这个通道感兴趣的事件。通过静态open方法创建。
4.1注册
通道可以通过它的register方法,将通道注册到选择器上。
SelectionKey
register(Selector sel, int ops)
Registers this channel with the given selector, returning a selection key.
abstract SelectionKey
register(Selector sel, int ops, Object att)
Registers this channel with the given selector, returning a selection key.
这个该方法会返回一个SeletctKey对象,但在这里我们通常忽略这个返回值。SeletctionKey对象内部包含了这个注册的通道和这个通道感兴趣的事件(ops参数),以及附带的对象(由att参数传递),这个附带的对象通常就是和这个通道相关的读写缓冲区。
4.2通道的选择与取消
abstract int
select()
Selects a set of keys whose corresponding channels are ready for I/O operations.
abstract int
select(long timeout)
Selects a set of keys whose corresponding channels are ready for I/O operations.
abstract int
selectNow()
Selects a set of keys whose corresponding channels are ready for I/O operations.
三个方法的返回值都表示就绪通道的数量。
select()方法是个阻塞方法,有通道就绪才会返回。