Netty 系列 一 NIO模型

Java NIO 的模型图 

1.一个selector对应一个线程来管理 

2.一个selector 管理多个channel

3.每个channel对应一个自己的buffer

4.程序切换到哪一个channel是有事件决定的 event 

5.selector会根据不同事件在多个channnel之间来切换

6.buffer是一个内存块 底层是一个数组

7.对channel的读写都是通过buffer来实现的

8.channel是双向的 可转变的

 

NIO编程案例 一个服务端 和多个客户端会话 

服务端会打印客户端上线下线的提示 还会将接受到的客户端的消息转发给其他的客户端  

客户端接受服务端的消息 向服务器发送消息

服务端代码

package com.example.netty.nio;

import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description: 服务器端  客户端上线提示  客户端下线提示 接收客户端的消息 并妆发给其他的客户端
 * @Auther:
 * @Date: 2020/3/18 09:42
 * @version:
 */
public class MyChatServer {
    private static ServerSocketChannel serverSocketChannel;
    private static Selector selector;
    private static final int PORT = 6667;
    private static String userName;

    public MyChatServer() {
        try {
            //获取选择器
            selector = Selector.open();
            // 获取ServerChannel
            serverSocketChannel = ServerSocketChannel.open();
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(PORT));
            // 设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            // 注册监听事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 获取服务端名字
            userName = serverSocketChannel.getLocalAddress().toString().substring(1);
            System.out.println("server is ok ..." + userName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void listen() throws Exception {
        System.out.println("开始监听客户端....");
        // 循环监听
        while (true){
            // 查看是否有事件 最多等待一秒钟
            int select = selector.select(1000);
            if (select > 0) {
                //使用迭代器方便 移除key
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()) {
                        // 可连接的事件 表示客户端已准备好链接服务器端
                        SocketChannel channel = serverSocketChannel.accept();
                        // 设置为非阻塞 链接事件处理 打印客户端 上线提示
                        channel.configureBlocking(false);
                        // 把channel 注册到 选择器 读取事件监听
                        channel.register(selector, SelectionKey.OP_READ);
                        String string = channel.getRemoteAddress().toString();
                        System.out.println("客户端上线.." + string);
                    }
                    if (selectionKey.isReadable()) {
                        // 读取数据 并将数据转发到其他的客户端
                        // 链接事件处理 打印客户端 上线提示
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        read(channel);
                    }
                    //移除这个key
                    iterator.remove();
                }
            }
        }
    }

    public static void read(SocketChannel channelSelf) {
        try {
            // 获取到客户端的消息
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            channelSelf.read(buffer);
            String msg = new String(buffer.array());
            System.out.println(channelSelf.getRemoteAddress().toString() + "消息内容" + msg);
            // 转发给其他的客户端
            Set<SelectionKey> keys = selector.keys();
            for (SelectionKey key:keys ){
                // 遍历所有注册到选择器上面的channel
                Channel channel = key.channel();
                // 排除自已以外的所有的socketChannel 转发消息
                if(channel instanceof SocketChannel && channel!=channelSelf){
                    System.out.println("服务器开始转发消息");
                    ByteBuffer wrap = ByteBuffer.wrap(msg.getBytes());
                    ((SocketChannel) channel).write(wrap);
                }
            }
        } catch (Exception e) {
            System.out.println("客户端离线.....");
        }
    }

    public static void main(String[] args) throws Exception{
        MyChatServer myChatServer= new MyChatServer();
        listen();
    }

}

客户端代码

package com.example.netty.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @Description: 向客户端发送消息并接受服务器端的消息
 * @Auther: shanshaowei
 * @Date: 2020/3/18 10:29
 * @version:
 */
public class MyChatClient {
    private static Selector selector;
    private static SocketChannel socketChannel;
    private static int PORT = 6667;
    private static String userName;

    public MyChatClient(){
        try{
            //创建Selector
            selector = Selector.open();
            //创建SocketChannel 去链接远程的ip和端口
            socketChannel= SocketChannel.open(new InetSocketAddress("127.0.0.1",PORT));
            // 设置为非阻塞
            socketChannel.configureBlocking(false);
            // 注册到选择器并监听
            socketChannel.register(selector, SelectionKey.OP_READ);
            userName = socketChannel.getLocalAddress().toString();
            System.out.println(userName+"is ok....");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void read()throws Exception{
        int select = selector.select();
        if(select>0){
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey next = iterator.next();
                if(next.isReadable()){
                    ByteBuffer allocate = ByteBuffer.allocate(1024);
                    SocketChannel channel = (SocketChannel)next.channel();
                    channel.read(allocate);
                    System.out.println("来自服务器的消息"+new String(allocate.array()));
                }
                iterator.remove();
            }
        }
    }

    public static void send(String msg) throws Exception{
        msg=userName+":"+msg;
        socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
    }

    public static void main(String[] args) throws Exception{
        //初始化客户端
        MyChatClient chatClient= new MyChatClient();
        // 创建一个线程 接受服务端信息
        new Thread(new Runnable() {
            @Override
            public void run() {
            // 不听的去读
                while (true){
                   try{
                       read();
                   }catch (Exception e){
                       e.printStackTrace();
                   }
                }
            }
        }).start();

        //发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextLine()) {
            String s = scanner.nextLine();
            send(s);
        }
    }

}

需要多个客户端就拷贝多个客户端的类 客户端控制台输入消息即可通知到服务器

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值