zoukankan      html  css  js  c++  java
  • NIO与Socket

    一、Socket 的使用

      1、单线程Socket的使用    

    /**
     * 单线程版本
     * 问题描述:只能服务单个客户端
     * 解决方案:多线程版本
     */
    public class Socket_V1 {
    	public static void main(String[] args) throws Exception {
    		ServerSocket serverSocket = new ServerSocket(1234);
    		System.out.println("启动服务器:");
    		while(true) {
    			System.out.println("等待客户端链接(阻塞)……");
    			Socket socket = serverSocket.accept();
    			System.out.println("有链接进来了……");
    			handle(socket);
    		}
    	}
    	private static void handle(Socket socket) {
    		try {
    			InputStream inputStream = socket.getInputStream();
    			byte[] b = new byte[1024];
    			String str = null;
    			while(true) {
    				str = null;
    				System.out.println("等待输入……");
    				int read = inputStream.read(b);
    				if(read == -1) {
    					System.out.println("客户端关闭。");
    					break;
    				}
    				str = new String(b);
    				System.out.println(str);
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

      2、多线程版本

    /**
     * 多线程版本
     * 问题描述:多线程开销大
     * 解决方案:NIO
     */
    public class Socket_V2 {
    
    	public static void main(String[] args) throws Exception {
    		ServerSocket serverSocket = new ServerSocket(1234);
    		// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    		ExecutorService threadPool = Executors.newFixedThreadPool(10);
    		System.out.println("启动服务器:");
    		while(true) {
    			System.out.println("等待客户端链接(阻塞)……");
    			final Socket socket = serverSocket.accept();
    			System.out.println("有链接进来了……");
    			//使用线程池来,支持并发
    			threadPool.execute(new Runnable() {
    				public void run() {
    					handle(socket);
    				}
    			});
    		}
    	}
    	private static void handle(Socket socket) {
    		try {
    			InputStream inputStream = socket.getInputStream();
    			byte[] b = new byte[1024];
    			String str = null;
    			while(true) {
    				str = null;
    				System.out.println("等待输入……");
    				int read = inputStream.read(b);
    				if(read == -1) {
    					System.out.println("客户端关闭。");
    					break;
    				}
    				str = new String(b);
    				System.out.println(str);
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

      3、NIO 

    /**
     * NIO版本
     * 原理:IO多路复用
     * NIO与旧Socket对比:
     * 	ServerSocketChannel	<-等价于->	ServerSocket
     * 	SocketChannel		<-等价于->	Socket
     * 	Selector   		选择器
     * 	SelectionKey 	事件类型
     */
    public class Socket_NIO {
    	public static void main(String[] args) throws Exception {
    		//获得Socket通道
    		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    		//将通道设置为非阻塞模式
    		serverSocketChannel.configureBlocking(false);
    		//将通道绑定到指定的端口
    		serverSocketChannel.socket().bind(new InetSocketAddress(1234));
    		//获取通道选择器
    		Selector selector = Selector.open();
    		//将连接事件,注册到选择器内(步骤1)
    		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    		System.out.println("服务器启动成功。端口已经监听……");
    		while(true) {
    			selector.select();//阻塞等待已经注册的事件
    			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
    			while(iterator.hasNext()) {
    				SelectionKey next = iterator.next();
    				iterator.remove();
    				if(next.isAcceptable()) { //如果是连接事件,注册读写事件
    					System.out.println("获取到连接事件。");
    					ServerSocketChannel sschannel = (ServerSocketChannel) next.channel();  //该强转是步骤1内存放的值
    					SocketChannel channel = sschannel.accept(); //获取客户端连接的通道
    					channel.configureBlocking(false);//设置为非阻塞
    					channel.register(selector, SelectionKey.OP_READ); //步骤2
    				}else if(next.isReadable()) { //如果是可读事件,则执行读操作
    					System.out.println("获取到可读事件。");
    					SocketChannel channel = (SocketChannel) next.channel(); //获取socket通道,该强转是步骤2内存放的值
    					ByteBuffer dst = ByteBuffer.allocate(1024); //缓冲区
    					int read = channel.read(dst);
    					if(read < 0 ) {
    						System.out.println("客户端关闭。");
    						next.cancel();
    						break;
    					}
    					System.out.println("客户端输入:"+new String(dst.array()));
    				} //还有其他事件……略
    			}
    		}
    	}
    }
    

    关于nio的一点理解:

      优点:少了线程切换的开销。(io多路复用)

        对比多线程模式,线程用完cpu时间片之后,就切换线程。 此时io阻塞的线程(不是就绪状态,无法获取cpu时间片),io非阻塞线程(网速很慢),只能获取到一部分数据,则浪费了相应的cpu时间片。 

  • 相关阅读:
    _purecall函数
    RaiseFailFastException函数
    windows系统的快速失败机制---fastfail
    悲伤的 C++ throw(…)
    STATUS_STACK_BUFFER_OVERRUN不一定是栈缓冲区溢出
    Windows是如何将64位Ntdll映射到32位进程的---转自简书
    学会springboot多环境配置方案不用5分钟
    优雅的使用springboot集成任务调度
    springboot-使用OpenAPI之后我再也没有写过接口文档
    我是如何做到springboot自动配置原理解析
  • 原文地址:https://www.cnblogs.com/chen--biao/p/9959996.html
Copyright © 2011-2022 走看看