Java NIO SocketChannel
SocketChannel 是连接到 TCP 网络套接字的通道。SocketChannel
有两种创建方式:
- 客户端打开一个
SocketChannel
并连接到 Internet 上某处的服务器。SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("IP地址", 80));
- 服务端传入连接到达时创建的
SocketChannel
。//调用accept方法接受连接,产生服务器端的SocketChannel SocketChannel socketChannel = serverSocketChannel.accept();
SocketChannel
可以通过调用该SocketChannel.close()
方法关闭;
SocketChannel
可以设置为非阻塞模式,调用 connect()
,read()
并write()是
异步模式的;
1、如果 处于
SocketChannel
非阻塞模式,并且调用了connect()
,则该方法可能会在建立连接之前返回。要判断连接是否建立,可以调用该finishConnect()
方法,如下所示:socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("IP地址", 80)); while(!socketChannel.finishConnect()){ //等待,或者做其他事情... }
2、在非阻塞模式下,该
write()
方法可能会在没有写入任何内容的情况下返回。因此需要write()
在循环中调用该方法。3、在非阻塞模式下,该
read()
方法可以在根本没有读取任何数据的情况下返回。因此,您需要注意返回的int
,来知道读取了多少字节。
Java NIO ServerSocketChannel
Java NIO ServerSocketChannel 是一个可以侦听传入 TCP 连接的通道,就像标准 Java 网络中的ServerSocket一样。该ServerSocketChannel
班位于java.nio.channels
包。
举例:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9000)); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //其他操作... }
打开一个ServerSocketChannel:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
关闭一个ServerSocketChannel:
serverSocketChannel.close();
监听接受连接:
while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //... }
非阻塞模式:
ServerSocketChannel
可以设置为非阻塞模式。在非阻塞模式下,该accept()
方法立即返回,因此如果没有传入连接到达,则可能返回 null。因此必须检查返回SocketChannel
的值是否为空。ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9000)); serverSocketChannel.configureBlocking(false); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel != null){ //... } }
使用NIO实现C/S简单通信
NIOServer:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class NIOServer {
private Selector selector;
private Charset charset;
private ServerSocketChannel serverSocketChannel;
public void run(String hostname, int port) throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
//将serverSocketChannel绑定到指定的指定的IP地址的端口上
serverSocketChannel.bind(new InetSocketAddress(hostname, port));
//设置serverSocketChannel以非阻塞方式工作
serverSocketChannel.configureBlocking(false);
//将serverSocketChannel注册到Selector对象
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务端"+serverSocketChannel.getLocalAddress()+"启动成功!");
charset = StandardCharsets.UTF_8;
while (selector.select() > 0) {
for (SelectionKey selectedKey : selector.selectedKeys()) {
//在键集中删除正在处理的selectedKey
selector.selectedKeys().remove(selectedKey);
if (selectedKey.isAcceptable()) {
//调用accept方法接受连接,产生服务器端的SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
//设置socketChannel以非阻塞方式工作
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
//设置当前键下一次响应的就绪状态
selectedKey.interestOps(SelectionKey.OP_ACCEPT);
}
if (selectedKey.isReadable()) {
//从selectedKey键中获取对应的Channel
SocketChannel socketChannel = (SocketChannel) selectedKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
StringBuilder content = new StringBuilder();
try {
while (socketChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
content.append(charset.decode(byteBuffer));
}
System.out.println("来自"+socketChannel.getRemoteAddress()+":"+content);
//业务逻辑
selectedKey.interestOps(SelectionKey.OP_READ);
}catch (IOException e ) {
e.printStackTrace();
selectedKey.cancel();
if (selectedKey.channel()!=null)
selectedKey.channel().close();
}
//业务逻辑
}
}
}
}
public static void main(String[] args) {
try {
new NIOServer().run("127.0.0.1",9000);
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIOClient:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class NIOClient {
private Selector selector;
private Charset charset;
private SocketChannel socketChannel;
public void start(String hostname, int port) throws IOException {
selector = Selector.open();
socketChannel = SocketChannel.open(new InetSocketAddress(hostname, port));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
charset = StandardCharsets.UTF_8;
new Thread(() -> {
try {
while (selector.select() > 0) {
for (SelectionKey selectedKey : selector.selectedKeys()) {
selector.selectedKeys().remove(selectedKey);
if (selectedKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) selectedKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
StringBuilder content = new StringBuilder();
while (socketChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
content.append(charset.decode(byteBuffer));
}
System.out.println("来自" + socketChannel.getRemoteAddress() + ":" + content);
//业务逻辑
selectedKey.interestOps(SelectionKey.OP_READ);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
//业务逻辑
socketChannel.write(charset.encode("..."));
}
public static void main(String[] args) {
try {
new NIOClient().start("127.0.0.1",9000);
} catch (IOException e) {
e.printStackTrace();
}
}
}