netty之TCP客户端与服务端非阻塞IO代码记录

一、客户端代码

package com.lcj.simulation.common.tcp;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

public class Client implements Runnable {
    public static AtomicInteger sendCount = new AtomicInteger(0);
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean stop;
    private String name;
    private static byte[] defaultBytes;
    public static int  size = 40960;

    static {
        defaultBytes =  new byte[size];
        for (int i = 0; i < size; i++) {
            defaultBytes[i] = (byte) ('q' + i);
        }
    }

    public Client(int port, String name) {
        this("localhost", port, name);
    }

    public Client(String host, int port, String name) {
        this.host = host;
        this.port = port;
        this.name = name;
    }

    public void init() {
        try {
            //打开一个选择器
            selector = Selector.open();
            //打开一个Socket监听通道
            socketChannel = SocketChannel.open();
            //设置该通道为非阻塞模式
            socketChannel.configureBlocking(false);
            //在非阻塞模式下,该方法在建立连接之前就会返回结果了,后续为了确认连接是否建立成功,可以调用finishConnect()
            socketChannel.connect(new InetSocketAddress(host, port));
            //订阅连接事件
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            stop = false;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        init();
        int i = 0;
        while (!stop) {
            try {
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    //判断是否连接到服务器
                    if (selectionKey.isConnectable()) {
                        //判断连接是否建立成功
                        if (socketChannel.finishConnect()) {
                            sendMsg(name + " Connect Success!");
                            socketChannel.register(selector, SelectionKey.OP_WRITE);
                        }
                    }
                    if (selectionKey.isWritable()) {
                        sendMsg(name + " is saying \"Hello World\"!" + i++);
                        Thread.sleep(1);
                    }
                    iterator.remove();
                }
                selectionKeys.clear();
            } catch (ConnectException e) {
                System.out.println("连接失败!");
                return;
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void sendMsg(String expression) throws IOException {
        byte[] bytes = Client.defaultBytes;
        sendCount.addAndGet(bytes.length);
        ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
        byteBuffer.put(bytes);
        //翻转缓冲区,执行的操作:
        //1.将limit的位置设为position之后的一个位置
        //2.将position的位置重置为0
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        //清空缓冲区
        byteBuffer.clear();
    }
}

二、服务端代码

package com.lcj.simulation.common.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

public class Server implements Runnable {
    private int port;
    private volatile boolean stop;
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;

    public static AtomicInteger receiveCount = new AtomicInteger(0);

    public Server(int port) {
        this.port = port;
    }

    public void init() {
        try {
            //打开一个选择器
            selector = Selector.open();
            //打开一个Server-Socket监听通道
            serverSocketChannel = ServerSocketChannel.open();
            //设置该通道为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            //将通道注册在选择器上面,并将准备连接状态作为通道订阅时间
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            stop = false;
            System.out.println("服务器已经启动,端口号:" + port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        init();
        while (!stop) {
            try {
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    //判断是否准备好接收新进入的连接
                    if (selectionKey.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                        //通过ServerSocketChannel的accept()创建SocketChannel实例
                        //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //设置为非阻塞
                        socketChannel.configureBlocking(false);
                        //在选择器注册,并订阅读事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    if (selectionKey.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        //创建byteBuffer,并开辟一个1M的缓冲区
                        ByteBuffer byteBuffer = ByteBuffer.allocate(Client.size);
                        //读取请求码流,返回读取到的字节数
                        int readBytes = socketChannel.read(byteBuffer);
                        //判断客户端是否断开
                        if (readBytes < 0) {
                            selectionKey.cancel();
                            socketChannel.close();
                            return;
                        }
                        //读取到字节,对字节进行编解码
                        if (readBytes > 0) {
                            //将缓冲区从写模式切换到读模式
                            byteBuffer.flip();
                            //根据缓冲区可读字节数创建字节数组
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            //向缓冲区读数据到字节数组
                            byteBuffer.get(bytes);
                            String expression = new String(bytes, "UTF-8");
//                            System.out.println("服务器收到消息:"+expression.length());
                            receiveCount.addAndGet(bytes.length);
                        }
                    }
                    iterator.remove();
                }
                selectionKeys.clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //selector关闭后会自动释放里面管理的资源
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

三、测试代码

package com.lcj.simulation.common.tcp;

public class TestNio {
    public static void main(String[] args) {
        Thread server = new Thread(new Server(10086));
        Thread client1 = new Thread(new Client(10086, "ONE"));
        Thread client2 = new Thread(new Client(10086, "TWO"));
        Thread client3 = new Thread(new Client(10086, "Three"));
        Thread client4 = new Thread(new Client(10086, "Four"));
        Thread client5 = new Thread(new Client(10086, "Five"));
        server.start();
        client1.start();
        client2.start();
        client3.start();
        client4.start();
        client5.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            while (true) {
                int i = Server.receiveCount.get();
                System.out.println("Server receiveCount = " + i / 1024.0 / 1024.0 + " MB/S");
                Server.receiveCount.set(0);


                System.out.println(" ============================================================ ");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(() -> {
            while (true) {
                int i2 = Client.sendCount.get();
                System.out.println("Client sendCount = " + i2 / 1024.0 / 1024.0 + " MB/S");
                Client.sendCount.set(0);
                System.out.println(" ============================================================ ");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唯荒

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值