使用NIO实现非阻塞Socket通信(Java)

Java NIO SocketChannel

SocketChannel 是连接到 TCP 网络套接字的通道。SocketChannel有两种创建方式:

  1. 客户端打开一个SocketChannel并连接到 Internet 上某处的服务器。
    SocketChannel socketChannel = SocketChannel.open(); 
    socketChannel.connect(new InetSocketAddress("IP地址", 80));
  2. 服务端传入连接到达时创建的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();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40100414/article/details/120908170
今日推荐