【ServerSocketChannel详解】

1 定义和特点

A selectable channel for stream-oriented listening sockets.

上述摘自java docs中定义:ServerSocketChannel是面向的监听socket套接字的可选择性通道

具有以下特点:

  • 具有阻塞和非阻塞两种模式

  • 可以注册到多路复用器上(且一般都与复用器配合使用)

  • 基于TCP连接

  • 需要绑定到特定端口

  • 线程安全的

ServerSocketChannel支持的可选参数:

参数名作用描述
SO_RCVBUF套接字接收缓冲区大小
SO_REUSEADDR重新使用地址

2 使用与相关API

ServerSocketChannel 的作用:

在服务器端监听新的客户端 Socket 连接,而且本身不传输数据

1.创建ServerSocketChannel

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">ServerSocketChannel</span> <span style="color:#000000">channel</span> <span style="color:#981a1a">=</span> <span style="color:#000000">ServerSocketChannel</span>.<span style="color:#000000">open</span>();  <span style="color:#aa5500">//创建ServerSocketChannel </span></span></span>

2.绑定到网络接口

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">channel</span>.<span style="color:#000000">bind</span>(<span style="color:#770088">new</span> <span style="color:#000000">InetSocketAddress</span>(<span style="color:#116644">9999</span>));  <span style="color:#aa5500">//绑定至本机的9999端口</span></span></span>

3.接收连接

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">SocketChannel</span> <span style="color:#000000">socketChannel</span> <span style="color:#981a1a">=</span> <span style="color:#000000">channel</span>.<span style="color:#000000">accept</span>();</span></span>

ServerSocketChannel的accept方法返回SocketChannel套接字通道,用于读取请求数据和写入响应数据。

ServerSocketChannel的阻塞和非阻塞的体现:

  • 阻塞模式:在调用accept方法后,将阻塞知道有新的socket连接时返回SocketChannel对象,代表新建立的套接字通道。

  • 非阻塞模式:在调用accept方法后,如果无连接建立,则返回null;如果有连接,则返回SocketChannel。

3 SocketChannel和ServerSocketChannel

SocketChannel提供了连接到socket(套接字)的通道,NIO中提供了类似于java.net包中对于网络操作的api的功能。既然已经有连接到Socket套接字的通道,可以主动发起连接、传输数据(client端)ServerSocketChannel的功能就是接收连接、接收数据的通道(server端)

下面演示一个简单的NIO模型的代码。

Server端:

public class NIOServer {
    public static void main (String[] args) throws IOException{
        // 创建服务器套接字通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 绑定本地地址
        serverSocketChannel.socket().bind(new InetSocketAddress(9999));
        // 设置非阻塞
        serverSocketChannel.configureBlocking(false);
        // 创建选择器
        Selector selector = Selector.open();
        // 注册服务器套接字通道
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功");
        // 循环处理客户端请求
        while (true){
            // 选择器如果没有可用的键,则阻塞
            if (selector.select(1000) == 0) {
                System.out.println("等待连接中...");
                continue;
            }
            // 获取可用的键
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 遍历可用的键
            selectionKeys.forEach(key->{
                try {
                    // 按照selectionKey中Key的不同类型对应不同的事件,做不同的处理
                    // 如果键是客户端连接请求
                    if (key.isAcceptable()) {
                        // 接受客户端连接
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        // 设置为非阻塞8
                        socketChannel.configureBlocking(false);
                        // 注册选择器
                        socketChannel.register(selector, SelectionKey.OP_READ,ByteBuffer.allocate(512));
                        System.out.println(socketChannel.toString()+"连接成功");
                    }
                    // 如果键是可读的
                    if (key.isReadable()) {
                        // 获取连接客户端的通道
                        SocketChannel socketChannel = (SocketChannel)key.channel();
                        // 获取缓冲区
                        ByteBuffer buffer = (ByteBuffer)key.attachment();
                        // 读取数据
                        socketChannel.read(buffer);
                 
                        System.out.println("form客户端" + new String(buffer.array()));
                    }
                    // 移除键,防止重复处理
                    selectionKeys.remove(key);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}

client端:

public class NIOClient {
    public static void main (String[] args) throws Exception{
        SocketChannel socketChannel = SocketChannel.open();
        System.out.println("socketChannel "+socketChannel.toString());
        //设置为非阻塞
        socketChannel.configureBlocking(false);
        //尝试连接服务器
        if (!socketChannel.connect(new java.net.InetSocketAddress("127.0.0.1",9999))){
            //如果连接失败,则循环尝试连接
            while (!socketChannel.finishConnect()){
                System.out.println("连接服务器中...");
            }
        }
        System.out.println("连接服务器成功");
        //向服务器发送消息
        String request = "Hello Server!";
        socketChannel.write(ByteBuffer.wrap(request.getBytes()));
        //线程休眠10秒
        Thread.sleep(10000);
    }
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值