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();
        }
    }
}

 

posted @ 2017-09-17 20:33  浮白斋主人  阅读(148)  评论(0编辑  收藏  举报