java NIO 实现 SocketChannel通信

NIO VS BIO

NIO: new inputstream/outputstream

BIO: blocked inputstream/outputstream

BIO是传统的IO,是阻塞的,在serversocket编程中,每个线程处理一个连接。

NIO是新IO,在非阻塞模式下,采用IO多路复用模型,节约系统线程、减少上下文切换,提高系统效率。


IO多路复用模型,一个线程管理多个连接,节省系统资源

java NIO 实现 SocketChannel通信_第1张图片


传统IO模型,在read,accepet时会阻塞,一个连接占用一个线程。

java NIO 实现 SocketChannel通信_第2张图片


看懂了上述两张图,也就弄懂了传统socket编程与新socket编程的区别及优缺点。


server端代码

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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class MyServerSocket {

	
	public static void main(String[] args) throws IOException {
		
		Charset charset = Charset.defaultCharset();
		
		ServerSocketChannel ssc = ServerSocketChannel.open();
		
		ssc.socket().bind(new InetSocketAddress(8888));
		ssc.configureBlocking(false);
		Selector selector = Selector.open();
		ssc.register(selector, SelectionKey.OP_ACCEPT);//serversocketchannel只能接受Accept类型的channel

		
		while(true){
			
			int selectResult = selector.select();//select第一次是阻塞的,后续是非阻塞的,所以后续select返回值都为0
			System.out.println("socket ready的数量:"+selectResult);
			
			
			if(selectResult>0){//选中的socket
				
				Set<SelectionKey> set = selector.selectedKeys();
				Iterator<SelectionKey> iterator = set.iterator();
			
				
				while(iterator.hasNext()){
					SelectionKey selectionKey = iterator.next();
					
					if(selectionKey.isAcceptable()){//serversocketchannel
						System.out.println("accept");
						
						ServerSocketChannel tempSSC = (ServerSocketChannel) selectionKey.channel();
						SocketChannel tempSC = tempSSC.accept();
						tempSC.configureBlocking(false);
						
						tempSC.register(selector, SelectionKey.OP_READ);
					}
					
					if(selectionKey.isConnectable()){
						System.out.println("connect");
					}
					
					if(selectionKey.isReadable()){
						System.out.println("read");
						
						SocketChannel tempSC = (SocketChannel) selectionKey.channel();
						ByteBuffer dst = ByteBuffer.allocate(15);//单位字节
						int readResult = tempSC.read(dst);
						while(readResult != -1 && readResult != 0){//流终止会返回0或-1
							dst.flip();
							System.out.println("数据是:"+charset.decode(dst));
							dst.clear();
							readResult = tempSC.read(dst);
						}
						
					}
					
					if(selectionKey.isWritable()){
						System.out.println("write");
					}
					
					iterator.remove();//就绪channel处理后,从迭代器中移除,下次select后,会接收到新的channel
				}
				
				
			}
		}
		
//			ssc.close();
	}
}

client端代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class MySocket {

	
	public static void main(String[] args) throws IOException {
		
		SocketChannel sc = SocketChannel.open(new InetSocketAddress(8888));
		
		int capacity = 10;//单位,字节
		ByteBuffer buffer = ByteBuffer.allocate(capacity);
		
		Scanner scanner = new Scanner(System.in);
		String content = null;
		
		while((content =scanner.nextLine()) != null){
			byte[] bytes = content.getBytes();
			buffer.put(bytes);
			
			buffer.flip();
			while(buffer.hasRemaining()){
				sc.write(buffer);
			}
		
			buffer.clear();
		}
		
		sc.close();
	}
}


运行

先启动server端,然后启动client端,client输入数据,回车,server端就会收到数据。同理再起一个client,输入数据,server端收到数据。

实现了一个select线程,负责多个连接(channel)。

java NIO 实现 SocketChannel通信_第3张图片

java NIO 实现 SocketChannel通信_第4张图片


代码说明

1、java NIO使用了IO复用模型,根据《unix网络编程》一书的说法,IO multiplexing,仍然是同步IO,因为在第二阶段,从内核copy数据到进程内时,进程是阻塞的。所以,如果想更深入的理解NIO,有必要研究此模型。

2、对照IO复用模型,NIO中有selector类,而且channel必须是非block的才能与selector一起使用,select未收到就绪channel之前是阻塞的,之后是非阻塞的,这点要注意。

3、server端的缓冲一定要比client端的缓冲大,避免出现乱码现象。否则,比如client端缓冲是4,server端缓冲是2,编码是utf8,汉字占用3个字节,客户端发送"a你",服务端以2个字节为单元解码,a不乱码,“你”,被分成两部分解码,所以出现俩?号



想到的问题

1、如果,对select到的channel处理速度过慢,就会影响到后续的通信。可以使用线程池处理后续数据的操作,提高性能

2、传统BIO(blocked IO)的“一连接一线程,处理速度的确快,但是面对高访问,比如1000个连接,处理起来就棘手了。通过NIO单select负责多channel(连接)节省了程和资源,从而解决此问题。当然,在访问连接不多的情况下,NIO性能不一定比BIO高。






你可能感兴趣的:(java,nio,IO模型,SocketChannel通信)