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  。

posted on 2019-05-16 14:56  zhangyukun  阅读(307)  评论(0编辑  收藏  举报

导航