zoukankan      html  css  js  c++  java
  • NIO--2-代码

    package com.study.nio;
    
    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.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    public class NIOServer {
        private static int port = 9995; //端口
    
        // 1.第一个是服务
        private static ServerSocketChannel server; 
        
        // 2.第二个是多路复用器
        private static Selector selector;
        
        // 3.第三个就是缓冲区buffer
        // 信息接收
        ByteBuffer rcBuffer = ByteBuffer.allocate(1024);
        // 信息写出
        ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
        
        // 4.维护一个事件标签集合,和selector配合使用
        Map<SelectionKey,String> sessionMsg = new HashMap<SelectionKey,String>();
        
        // 初始化
        public NIOServer() throws IOException{
            server = ServerSocketChannel.open(); // 打开服务端,服务端临听端口
            server.socket().bind(new InetSocketAddress(port)); // 
            server.configureBlocking(false); //设置为非阻塞,默认是true
            selector = Selector.open(); //实例化多路复用器
            
            //server注册上多路复用器之后,多路复用器才会为server工作,实现一个server为成千上万个client工作
            //selector和事件标签Selectionkey是一起应用的,第一次服务器,是接受,Accept
            server.register(selector, SelectionKey.OP_ACCEPT); 
            System.out.println("NIO服务器已启动,且监听的端口是" + port); //this.port范围加大
        }
        
        // 监听请求方法
        public void listen() throws IOException{
            while(true){
                // 进来就是通过多路复用器selector,来看是否有注册事件
                int eventCount = selector.select(); // 已注册事件数量
                if(eventCount == 0){
                    // selector继续轮询,NIO的内部机制就是不断轮询注册到selector上面的多个channel
                    continue;
                }
                
                // 拿到事件集合,SelectorKey的作用就是获取这些就绪通道的集合
                Set<SelectionKey> keys = selector.selectedKeys();
                
                //迭代
                Iterator<SelectionKey> iterator = keys.iterator();
                while(iterator.hasNext()){
                    SelectionKey key = iterator.next(); //这个事件就是客户端的一个读或者写
                    process(key); //处理事件
                    //处理完后,事件移出集合
                    iterator.remove();
                }
            }
        }
        
        // 处理客户端事件
        private void process(SelectionKey key) {
            // 处理客户端请求,定义客户端对象
            SocketChannel client = null;
            try {
                
                
                // 判断key是否是有效的
                if(key.isValid() && key.isAcceptable()){
                    client = server.accept(); //接收一个客户端
                    client.configureBlocking(false); //设置为非阻塞
                    client.register(selector , SelectionKey.OP_READ); //读取客户端请求,事件标签类型改为读取
                
                } else if(key.isValid() && key.isReadable()){ //是否是可读的
                    // 读到缓冲区
                    rcBuffer.clear();
                    client = (SocketChannel)key.channel(); // 拿到客户端通道
                    int len = client.read(rcBuffer); //读取的长度
                    if(len > 0){ //读到内容
                        String msg = new String(rcBuffer.array() , 0 ,len);
                        System.out.println("服务端收到msg:"+msg);
                        sessionMsg.put(key, msg);
                        // 告诉selector,已读完,下次可以写数据
                        client.register(selector, SelectionKey.OP_WRITE);
                    }
                }else if(key.isValid() && key.isWritable()){
                    if(!sessionMsg.containsKey(key)){ //是否有消息需要回
                        return;
                    }
                    
                    //回复消息
                    client = (SocketChannel)key.channel(); // 拿到客户端通道
                    sendBuffer.clear();
                    sendBuffer.put(new String(sessionMsg.get(key)+ "你好,你的请求已完成").getBytes());
                    
                    //设置读取位
                    sendBuffer.flip();
                    
                    // 缓冲区的内容写出去
                    client.write(sendBuffer);
                    
                    // 再次注册入selector
                    client.register(selector, SelectionKey.OP_READ);
                }
            }  catch (IOException e) {
                try { //读取key事件时,遇到客户端异常下线,不会引起异常
                    key.cancel();
                    client.socket().close();
                    client.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            
        }
        
        public static void main(String[] args) {
            try {
                NIOServer server = new NIOServer();
                server.listen();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    CLIENT

    package com.study.nio;
    
    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.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    public class NIOClient {
        private static int port = 9995; //端口
    
        // 1.第一个是服务
        private static ServerSocketChannel clientC; 
        
        // 2.第二个是多路复用器
        private static Selector selector;
        
        // 3.第三个就是缓冲区buffer
        // 信息接收
        ByteBuffer rcBuffer = ByteBuffer.allocate(1024);
        // 信息写出
        ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
        
        // 4.维护一个事件标签集合,和selector配合使用
        Map<SelectionKey,String> sessionMsg = new HashMap<SelectionKey,String>();
        
        // 初始化
        public NIOClient() throws IOException{
            clientC = ServerSocketChannel.open(); // 打开服务端,服务端临听端口
            clientC.socket().bind(new InetSocketAddress(port)); // 
            clientC.configureBlocking(false); //设置为非阻塞,默认是true
            selector = Selector.open(); //实例化多路复用器
            
            //server注册上多路复用器之后,多路复用器才会为server工作,实现一个server为成千上万个client工作
            //selector和事件标签Selectionkey是一起应用的,第一次服务器,是接受,Accept
            clientC.register(selector, SelectionKey.OP_ACCEPT); 
            System.out.println("NIO服务器已启动,且监听的端口是" + port); //this.port范围加大
        }
        
        // 监听请求方法
        public void listen() throws IOException{
            while(true){
                // 进来就是通过多路复用器selector,来看是否有注册事件
                int eventCount = selector.select(); // 已注册事件数量
                if(eventCount == 0){
                    // selector继续轮询,NIO的内部机制就是不断轮询注册到selector上面的多个channel
                    continue;
                }
                
                // 拿到事件集合,SelectorKey的作用就是获取这些就绪通道的集合
                Set<SelectionKey> keys = selector.selectedKeys();
                
                //迭代
                Iterator<SelectionKey> iterator = keys.iterator();
                while(iterator.hasNext()){
                    SelectionKey key = iterator.next(); //这个事件就是客户端的一个读或者写
                    process(key); //处理事件
                    //处理完后,事件移出集合
                    iterator.remove();
                }
            }
        }
        
        // 处理客户端事件
        private void process(SelectionKey key) {
            // 处理客户端请求,定义客户端对象
            SocketChannel client = null;
            try {
                
                
                // 判断key是否是有效的
                if(key.isConnectable()){
                    client = (SocketChannel)key.channel(); // 拿到客户端通道
                    client.configureBlocking(false); //设置为非阻塞
                    client.register(selector , SelectionKey.OP_READ); //读取客户端请求,事件标签类型改为读取
                
                } else if(key.isValid() && key.isReadable()){ //是否是可读的
                    // 读到缓冲区
                    rcBuffer.clear();
                    client = (SocketChannel)key.channel(); // 拿到客户端通道
                    int len = client.read(rcBuffer); //读取的长度
                    if(len > 0){ //读到内容
                        String msg = new String(rcBuffer.array() , 0 ,len);
                        sessionMsg.put(key, msg);
                        // 告诉selector,已读完,下次可以写数据
                        client.register(selector, SelectionKey.OP_WRITE);
                    }
                }else if(key.isValid() && key.isWritable()){
                    if(!sessionMsg.containsKey(key)){ //是否有消息需要回
                        return;
                    }
                    
                    //回复消息
                    client = (SocketChannel)key.channel(); // 拿到客户端通道
                    sendBuffer.clear();
                    sendBuffer.put(new String(sessionMsg.get(key)+ "你好,你的请求已完成").getBytes());
                    
                    //设置读取位
                    sendBuffer.flip();
                    
                    // 缓冲区的内容写出去
                    client.write(sendBuffer);
                    
                    // 再次注册入selector
                    client.register(selector, SelectionKey.OP_READ);
                }
            }  catch (IOException e) {
                try { //读取key事件时,遇到客户端异常下线,不会引起异常
                    key.cancel();
                    client.socket().close();
                    client.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            
        }
        
        public static void main(String[] args) {
            try {
                NIOServer server = new NIOServer();
                server.listen();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 相关阅读:
    Scons 三
    Scons 二
    vs code插件
    Scons一
    实例演示 C# 中 Dictionary<Key, Value> 的检索速度远远大于 hobbyList.Where(c => c.UserId == user.Id)
    ASP.NET Core 如何用 Cookie 来做身份验证
    如何设计出和 ASP.NET Core 中 Middleware 一样的 API 方法?
    小记编程语言的设计
    解决 VS2019 打开 edmx 文件时没有 Diagram 视图的 Bug
    一款回到顶部的 jQuery 插件,支持 Div 中的滚动条回到顶部
  • 原文地址:https://www.cnblogs.com/fubaizhaizhuren/p/7536994.html
Copyright © 2011-2022 走看看