zoukankan      html  css  js  c++  java
  • socker TCP UDP BIO NIO

    BIO:  Java 1.4 以前只有之中方式。

    bio:阻塞式IO, 一个 socker 连接占用一个 线程。如果 IO 阻塞,会在传输速度限制,这个线程也会一直等待在这里,等待从socker 的 IO 流 中读写数据。

      Java 基于  socker 的 连接方式都是 BIO,都是阻塞式的IO。

      TCP:

           server:  

              

    ServerSocket  ss = new ServerSocket(20000);
    		while( true ) {
    			Socket socket = ss.accept();
    			List<String> datas = IOUtils.readLines( socket.getInputStream() );
    			System.out.println( datas.get(0) );
    			socket.close();
    			
    		}
    

          cilent:

             

    		Socket socket = new Socket("127.0.0.1", 20000);
    		
    		IOUtils.write("hello", socket.getOutputStream() );
    		socket.getOutputStream().flush();
    		socket.getOutputStream().close();
    		
    		socket.close();
    

      

    原理图:

      UDP:

          server:

            

    DatagramSocket  ds = new DatagramSocket(20000);
    		while( true ) {
    			byte[] buf = new byte[1024];
    			DatagramPacket dp = new DatagramPacket(buf, buf.length);
    			ds.receive( dp );
    			System.out.println( new String(dp.getData())   );
    		}
    

          client:

            

    	DatagramSocket  ds = new DatagramSocket();
    		String data = "hello";
    		DatagramPacket dp = new DatagramPacket(data.getBytes(),data.length(),InetAddress.getLocalHost(),20000);
    		ds.send(dp);
    

      

    NIO: Java 1.4 以后支持的IO方式

    NIO: 非阻塞式的IO, NIO 不再是 传统socker 方式。 一个 IO请求过来,会有一个线程,来处理这个IO请求,但是如果这个IO阻塞,那么这个线程会被处理别的 IO请求的 的事情。如果阻塞的IO的操作完成(完成一个块数据写入缓冲区),那么就会有一个线程分配过来处理这部分数据。

    selecter: NIO 不再是  传统socke的概念。 socker 一个 连接,就固定一个 线程来处理,并且这个线程值服务这个 socker。但是 NIO 改变了这种IO 模型, selecter  里面跑着一个独立的线程,这个线程管理 一些渠道( channel , 有点 类似 socker  ),如果 某个

    渠道的 数据准备好了,那么 selecter 就会分配一个线程来 读写 这个channet 的数据。

    buffer: 前面 一直强调 当数据准备好了,什么样才叫数据准备好了?  socker 方式的 io 模式是一个 字节 一个字节读写的。 NIO 是通过 一块一块读写的(  一块就是 多个字节 ),但是这个这个  一块数据是 需要 一个临时存放区域的,这就是buffer 。当一个 块 数据满了以后,就分配 线程 来执行这个请求。

    channel: 相当于以前的  流的  概念,可以 李杰成一个管道。BIO 一个连接一个线程,并且在socker结束以前一直占用着这个线程。NIO 是一个 也是一个连接一个线程。但是这个线程不会一直 等待这个 连接的IO操作。

    例子代码:

        server:

    package comcxygg.test.nio;
    
    
    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.nio.charset.Charset;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.UUID;
     
    public class NioServer {
        public static void main(String[] args) throws Exception {
            /**
             * 开启一个服务端
             * 设置为非阻塞
             * 绑定端口号
             */
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(8080));
     
            System.out.println("serverSocketChannel:" + serverSocketChannel.hashCode() );
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //UUID->客户端连接
            Map<String,SocketChannel> clientMap = new HashMap<>();
     
            while (true) {
                selector.select();
     
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                selectionKeys.forEach(selectionKey -> {
     
                    try {
                        if (selectionKey.isAcceptable()) {
                            /**
                             * 服务端接收到连接
                             * 保存接收到的客户端连接
                             */
                            ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                            System.out.println("server:" + server.hashCode() );
                            
                            SocketChannel socketChannel = server.accept();
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector,SelectionKey.OP_READ);
                            String key = UUID.randomUUID().toString();
                            clientMap.put(key,socketChannel);
                            System.out.println(socketChannel.getRemoteAddress()+"连接上了服务器");
                        } else if (selectionKey.isReadable()) {
                            /**
                             * 读取客户端消息
                             * 转发到所有客户端
                             */
                            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                            
                            System.out.println( "服务器端socketChannel:" + socketChannel.hashCode());
                            try {
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                int len = socketChannel.read(buffer);
     
                                if (len > 0) {
                                    buffer.flip();
                                    Charset charset = Charset.forName("UTF-8");
                                    String receiveMsg = String.valueOf(charset.decode(buffer).array());
                                    String key = null;
                                    for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) {
                                        if (entry.getValue() == socketChannel) {
                                            key = entry.getKey();
                                            break;
                                        }
                                    }
                                    String sendMsg = key + ":" + receiveMsg;
                                    System.out.println(sendMsg);
                                    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                    writeBuffer.put(sendMsg.getBytes());
                                    writeBuffer.flip();
                                    clientMap.get(key).write( writeBuffer );
                                  /*  for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) {
                                        ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                        writeBuffer.put(sendMsg.getBytes());
                                        writeBuffer.flip();
                                        entry.getValue().write(writeBuffer);
                                    }*/
                                }
                            }catch (Exception e) {
                                e.printStackTrace();//java.io.IOException: 远程主机强迫关闭了一个现有的连接。
                                socketChannel.close();
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
                selectionKeys.clear();
            }
     
        }
    }

        client:

             

    package comcxygg.test.nio;
    
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    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.Set;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class NioClient {
       public static void main(String[] args) throws Exception {
           /**
            * 开启一个客户端
            * 设置为非阻塞
            * 连接到服务器
            */
           SocketChannel socketChannel = SocketChannel.open();
           socketChannel.configureBlocking(false);
           socketChannel.connect(new InetSocketAddress("localhost",8080));
    
           Selector selector = Selector.open();
           socketChannel.register(selector, SelectionKey.OP_CONNECT);
    
           System.out.println( "socketChannel:" + socketChannel.hashCode() );
           
           while (true) {
               selector.select();
    
               Set<SelectionKey> selectionKeys = selector.selectedKeys();
               for (SelectionKey selectionKey : selectionKeys) {
                   if (selectionKey.isConnectable()) {
                       /**
                        * 客户端已连接
                        * 开启一个线程监听控制台输入
                        */
                       SocketChannel client = (SocketChannel) selectionKey.channel();
                       System.out.println( "client1:" + client.hashCode() );
                       if (client.isConnectionPending()) {
                           client.finishConnect();
                       }
                       client.register(selector,SelectionKey.OP_READ);
                       ExecutorService executor = Executors.newSingleThreadExecutor();
                       System.out.println(socketChannel.getLocalAddress()+"连上了服务器");
                       ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                       executor.submit(()->{
                           try {
                               while (true) {
                                   writeBuffer.clear();
                                   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                                   String line = reader.readLine();
                                   writeBuffer.put(line.getBytes());
                                   writeBuffer.flip();
                                   client.write(writeBuffer);
                               }
                           }catch (Exception e) {
                               e.printStackTrace();
                           }
                       });
                   } else if (selectionKey.isReadable()) {
                       
                       
                       /**
                        * 打印服务端消息
                        */
                       SocketChannel client = (SocketChannel) selectionKey.channel();
                       System.out.println( "client2:" + client.hashCode() );
                       ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                       int len = client.read(readBuffer);
                       System.out.println(new String(readBuffer.array(),0,len));
                   }
               }
               selectionKeys.clear();
           }
       }
    }

        BIO 图:

    NIO 的 UDP 协议使用:

       别的基本一样 只是 渠道使用的 DatagramChannel  。

  • 相关阅读:
    【java8】慎用java8的foreach循环(作废)
    【Java并发系列03】ThreadLocal详解
    【Java并发系列04】线程锁synchronized和Lock和volatile和Condition
    【Java并发系列02】Object的wait()、notify()、notifyAll()方法使用
    【Java并发系列01】Thread及ThreadGroup杂谈
    java安全管理器SecurityManager入门
    【DWR系列06】- DWR日志及js压缩
    时间插件
    springMVC中Restful支持
    面向接口编程
  • 原文地址:https://www.cnblogs.com/cxygg/p/10875667.html
Copyright © 2011-2022 走看看