• Java Socket编程(五)NIO



    一、服务器端的处理模式

    1.迭代服务器

    服务器只有处理完了当前用户的请求后,才会处理下一请求,因此是
    迭代式的,在同一线程内处理。

    ServerSocket serverSocket = new ServerSocket(port);
    while(true) {
         Socket clientSocket = serverSocket.accept();
         ...
    }


    2.一客户一线程

    对每个连接的客户端都新建一个线程来处理它的请求。这种处理方式
    的缺点很明显,当创建出来的同时运行的线程过多时,操作系统大量
    时间都耗费在线程的切换和状态维护上,而非线程内的请求处理。

    ServerSocket serverSocket = new ServerSocket(port);
    while(true) {
         Socket clientSocket = serverSocket.accept();
         new HandlerThread(clientSocket).start();
         ...
    }


    3.线程池

    使用固定数目的线程来监听请求,或者使用JDK的Executor线程池。相比
    前面两种方法,这种处理方式在并发与性能之间达到了一个平衡,但是
    它也是有自身的缺点,具体见后文的描述。

    final ServerSocket serverSocket = new ServerSocket(port);
    for (int i = 0; i < poolSize; i++) {
         new Thread() {
              public void run() {
                   while (true) {
                        Socket clientSocket = serverSocket.accept();
                        ...
                   }
              }
         }.start();
    }


    4.缺点

    类似上述这些基于阻塞式Socket和多线程的处理方式都有很大的缺点:

    1.线程池的大小限制了系统可以同时服务的客户端总数。

    2.如果想要保证某些连接优先获得服务,线程就很难做到。

    3.如果这些线程需要读写共享的资源的话,还需要锁或其他互斥机制来同步。


    二、NIO中的Select机制

    通过NIO,我们可以很方便地在Java中实现Select机制。Select机制比上述的
    处理方式好在哪呢?

    首先,在Select机制中,我们不需要许多的线程来应对无数客户端的请求。我们
    通过一次select()调用可以得到一组客户端,而这些客户端都是准备好读写的,
    因此在后续的读写操作都是非阻塞的。所以这一组准备就绪的客户端读写请求就
    都交给一个Selector类来处理了,完全的单线程,没有了上面的那些缺点!

    此外,定长的Buffer缓冲类替代了流的概念,所以NIO里没有了输入输出流的身影,
    而是将数据保存到Buffer对象中后,直接写入到Channel中。读取时,也是直接从
    Channel中读取出Buffer。
    public class TCPNonblockingClientTest {
    
    	public static void main(String[] args) throws Exception {
    
    		SocketChannel channel = SocketChannel.open();
    		channel.configureBlocking(false);
    		
    		if (!channel.connect(new InetSocketAddress("localhost", 1234))) {
    			while (!channel.finishConnect())
    				System.out.print(".");
    			System.out.println();
    		}
    		
    		String message = "helloselector";
    		ByteBuffer writeBuf = ByteBuffer.wrap(message.getBytes());
    		ByteBuffer readBuf = ByteBuffer.allocate(message.length());
    		int totalBytesRcvd = 0;
    		int bytesRcvd;
    		while (totalBytesRcvd < message.length()) {
    			
    			if (writeBuf.hasRemaining())
    				channel.write(writeBuf);
    			
    			if ((bytesRcvd = channel.read(readBuf)) == -1)
    				throw new SocketException("Connection closed");
    			
    			totalBytesRcvd += bytesRcvd;
    			System.out.print(".");
    		}
    		
    		System.out.println("Received: " + new String(readBuf.array(), 0, totalBytesRcvd));
    		channel.close();
    	}
    
    }

    public class TCPSelectorServerTest {
    
    	public static void main(String[] args) throws Exception {
    		
    		Selector selector = Selector.open();
    		
    		int[] ports = { 1234, 5678 };
    		for (int port : ports) {
    			ServerSocketChannel listenChannel = ServerSocketChannel.open();
    			listenChannel.socket().bind(new InetSocketAddress("localhost", port));
    			listenChannel.configureBlocking(false);
    			listenChannel.register(selector, SelectionKey.OP_ACCEPT);
    		}
    		
    		while (true) {
    			
    			if (selector.select(3000) == 0) {
    				System.out.print(".");
    				continue;
    			}
    			
    			Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
    			while (keyIter.hasNext()) {
    				SelectionKey key = keyIter.next();
    				
    				if (key.isAcceptable()) {
    					SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
    					clientChannel.configureBlocking(false);
    					clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(32));
    				}
    				
    				if (key.isReadable()) {
    					SocketChannel clientChannel = (SocketChannel) key.channel();
    					ByteBuffer buffer = (ByteBuffer) key.attachment();
    					long bytesRead = clientChannel.read(buffer);
    					if (bytesRead == -1)
    						clientChannel.close();
    					else
    						key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
    				}
    				
    				if (key.isValid() && key.isWritable()) {
    					ByteBuffer buffer = (ByteBuffer) key.attachment();
    					buffer.flip();
    					SocketChannel clientChannel = (SocketChannel) key.channel();
    					clientChannel.write(buffer);
    					
    					// No left, no longer interest in write
    					if (!buffer.hasRemaining())
    						key.interestOps(SelectionKey.OP_READ);
    					
    					buffer.compact();
    				}
    				
    				keyIter.remove();
    			}
    			
    		}
    		
    	}
    	
    
    }

  • 相关阅读:
    Web安全测试
    性能测试---并发用户计算
    浅谈软件性能测试中关键指标的监控与分析
    fiddler抓包——IOS
    Linux下查看CPU型号,内存大小,硬盘空间,进程等的命令(详解)
    windows 升级pip
    java 使用Iterator 迭代器遍历AList、Set、Map
    springboot 封装redis工具类
    idea 取消@Autowired 不建议字段注入的警告
    查看服务器相关
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157824.html
Copyright © 2020-2023  润新知