一.前言
前篇文章中了解了SocketChannel:提供了连接到套接字通道,从某种层面而言,NIO中提供了类似于java.net包中对于网络操作的api的功能。既然已经有连接到Socket套接字的通道,可以主动发起连接、传输数据,还缺少接收连接(client端)。不言而喻,还缺少接收连接,接收数据的通道(server端)。这篇文章就主要介绍监听套接字的通道ServerSocketChannel。
二.ServerSocketChannel
A selectable channel for stream-oriented listening sockets.
上述摘自java docs中定义:ServerSocketChannel是面向流的监听socket套接字的可选择性通道。从定义中可以看出以下几点:
- 具有阻塞和非阻塞两种模式
- 可以注册到多路复用器上
- 基于TCP连接
- 需要绑定到特定端口上
ServerSocketChannel可以被无参的open()
方法创建。但是改方法只是创建了一个ServerSocketChannel对象,并没有进行绑定操作,仍需要调用bind()
方法进行绑定,使之监听某个套接字。未进行绑定的ServerSocketChannel调用accept()
,将会抛出NotYetBoundException
异常。
ServerSocketChannel支持的可选参数:
参数名 | 作用描述 |
---|---|
SO_RCVBUF | 套接字接收缓冲区大小 |
SO_REUSEADDR | 重新使用地址 |
ServerSocketChannel支持两种模式:阻塞模式和非阻塞模式。且是线程安全的
三.ServerSocketChannel使用
1.创建ServerSocketChannel
ServerSocketChannel channel = ServerSocketChannel.open(); //创建ServerSocketChannel
2.绑定到本机网络接口
channel.bind(new InetSocketAddress(8091)); //绑定至8091端口
3.接收连接
SocketChannel socketChannel = channel.accept();
ServerSocketChannel的accept方法返回SocketChannel套接字通道,用于读取请求数据和写入响应数据。
ServerSocketChannel的阻塞和非阻塞体现在这里:
- 阻塞模式:在调用accept方法后,将阻塞知道有新的socket连接时返回SocketChannel对象,代表新建立的套接字通道。
- 非阻塞模式:在调用accept方法后,如果无连接建立,则返回
null
;如果有连接,则返回SocketChannel。
四.简单的client-server
创建简单的server:
public void createServerSocketChannel() throws IOException {
ServerSocketChannel channel = ServerSocketChannel.open();
channel.bind(new InetSocketAddress(8091));
while (true) {
SocketChannel socketChannel = channel.accept();
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
int count = socketChannel.read(byteBuffer);
while(count != -1) {
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
System.out.print((char) byteBuffer.get());
}
byteBuffer.clear();
count = socketChannel.read(byteBuffer);
}
System.out.println();
}
}
创建简单client:
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(
new InetSocketAddress("10.17.83.11", 8091));
String msg = "ok";
socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
SocketChannel socketChannel3 = SocketChannel.open(
new InetSocketAddress(8091));
socketChannel3.write(ByteBuffer.wrap(msg.getBytes()));
System.out.println("s3");
SocketChannel socketChannel4 = SocketChannel.open(
new InetSocketAddress(8091));
socketChannel4.write(ByteBuffer.wrap(msg.getBytes()));
System.out.println("s4");
socketChannel4.close();
socketChannel3.close();
socketChannel.close();
}
ServerSocketChannel基本上不会单独使用绝大多数情况下都是配合多路复用Selector选择器共同使用。这里只关注ServerSocketChannel本身,关于其与多路复用混合使用,后面再介绍Selector时再做详细介绍!