NIO与BIO服务器端对比

本文利用NIO实现一个重复回复,客户端发送什么信息,客户端就会收到什么信息。

主要是理解NIO与BIO的区别。客户端采用telnet进行测试,以下连接是Telnet安装的方法。

Telnet的简单使用_武汉小喽啰的博客-CSDN博客_telnet

注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端

注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端

注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端

可以使用?/help 进行命令查看命令很少的。

BIO即阻塞IO测试,发现以下两个地方会被阻塞,第一个客户端连接,第二个是客户端输入信息

  • Socket socket = serverSocket.accept();
  • InputStream is = socket.getInputStream();

客户端连接服务端流程:

【第一步】启动服务器--服务器运行到accept命令阻塞

【第二步】连接客户端,服务器接收客户端,阻塞到 getInputStream() 等待客户端发送信息。

可以看出来,一个线程为客户端服务,直到客户端断开连接

【代码】

package cn.msf.bio;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author: msf
 * @date: 2022/11/22
 */
public class BIO {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(10101);
            System.out.println("服务端启动");
            while (true) {
                // 获取一个套接字(阻塞)
                Socket socket = serverSocket.accept();
                System.out.println("来了一个客户端");
                hander(socket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void hander(Socket socket) {
        byte[] bytes = new byte[1024];
        int len = 0;
        try {
            // 阻塞等待。
            InputStream is = socket.getInputStream();
            // 将信息读到bytes中。
            while ((len = is.read(bytes)) != -1) {
                System.out.println(new String(bytes, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭socket");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

NIO实现测试

【第一步】建立服务器端,这里使用的命令是

ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 设置为不阻塞
serverChannel.configureBlocking(false);
        // 设置socket的连接接口
serverChannel.socket().bind(new InetSocketAddress("localhost",port));

【第二步】开启selector相当找了一个服务员,为客户端服务

// 启动一个选择器
        selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

 【第三步】监听客户端连接

 public void listen() throws IOException {
        System.out.println("服务端启动成功");
        while (true) {
            // 当服务端注册的时间到达后,返回;否则会一直阻塞
            selector.select();
            selector.wakeup();
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = ite.next();
                ite.remove();
                // 处理事件的类型
                handler(key);
            }
        }
    }

【第四步】为不同客户端处理不同的事件。

可以达到一对多服务,不会想BIO一样阻塞在getinputstream哪里只等待一个客户端

 

private void handler(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            // 说明是注册事件
            handlerAccept(key);
        } else if (key.isReadable()) {
            handlerRead(key);
        }
    }

【完整代码】 

package cn.msf.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;

/**
 * @author: msf
 * @date: 2022/11/23
 */
public class NIOServer {
    private Selector selector;

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer(8000);
        server.listen();
    }


    public void initServer(int port) throws IOException {
        // 获得一个ServerSocket 通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 设置为不阻塞
        serverChannel.configureBlocking(false);
        // 设置socket的连接接口
        serverChannel.socket().bind(new InetSocketAddress("localhost",port));

        // 启动一个选择器
        selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void listen() throws IOException {
        System.out.println("服务端启动成功");
        while (true) {
            // 当服务端注册的时间到达后,返回;否则会一直阻塞
            selector.select();
            selector.wakeup();
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = ite.next();
                ite.remove();
                // 处理事件的类型
                handler(key);
            }
        }
    }

    private void handler(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            // 说明是注册事件
            handlerAccept(key);
        } else if (key.isReadable()) {
            handlerRead(key);
        }
    }

    public void handlerAccept(SelectionKey key) throws IOException {
        // 当有事件来了,获得之前的服务端
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        // 获得客户端的通道--相当于客户端
        SocketChannel channel = server.accept();
        channel.configureBlocking(false);
        System.out.println("客户端连接");
        channel.register(this.selector,SelectionKey.OP_READ);
    }

    private void handlerRead(SelectionKey key) throws IOException {
       // 服务器可读消息,得到事件发生的Socket通道。
        SocketChannel channel = (SocketChannel) key.channel();
        // 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = channel.read(buffer);
        if (read > 0) {
            byte[] data = buffer.array();
            String msg = new String(data).trim();
            System.out.println("服务器收到消息: " + msg);
            ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8));
            // 将消息发送给客户端
            channel.write(outBuffer);
        } else {
            channel.close();
        }

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兜兜转转m

一毛钱助力博主实现愿望

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值