NIOServer端和Client端代码案例

本文深入探讨NIO(Non-blocking I/O)通讯机制,详细解析客户端与服务器端的交互过程,包括SocketChannel的非阻塞配置、选择器(Selector)的使用、事件监听与处理等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NIO通讯Client端

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.SocketChannel;
import java.util.Iterator;

public class NIOClient{

	public static void main(String[] args)

	{
		for (int i = 0; i < 10; i++) {

			new Worker().start();

		}

	}

	static class Worker extends Thread {
		@Override
		public void run(){
			SocketChannel channel =	null;
			Selector selector = null;
			try{
				//SocketChannel,一看底层就是封装了一个Socket
				channel = SocketChannel.open();
				//SocketChannel是连接到底层的Socket网络
				//数据通道就是负责基于网络读写数据的
				channel.configureBlocking(false);
				channel.connect(new InetSocketAddress("localhost",9000));
				//后台一定是tcp三次握手建立网络连接
				selector = Selector.open();
				// 监听Connect这个行为
				channel.register(selector,SelectionKey.OP_CONNECT);
				while (true) {
					//selector多路复用机制的实现  循环去遍历各个注册的Channel
					selector.select();
					Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
					while (keysIterator.hasNext()) {
						SelectionKey key = (SelectionKey) keysIterator.next();
						keysIterator.remove();
						//如果发现返回的时候一个可连接的消息 走到下面去接受数据
						if (key.isConnectable()) {
							channel = (SocketChannel) key.channel();
							if (channel.isConnectionPending()) {
								channel.finishConnect();
								//接下来对这个SocketChannel感兴趣的就是人家server给你发送过来的数据了
								//READ事件,就是可以读数据的事件
								//一旦建立连接成功了以后,此时就可以给server发送一个请求了
								ByteBuffer buffer = ByteBuffer.allocate(1024);
								buffer.put("你好".getBytes());
								buffer.flip();
								channel.write(buffer);
							}
							channel.register(selector,SelectionKey.OP_READ);
						}
						//这里的话就时候名服务器端返回了一条数据可以读了
						else if (key.isReadable()) {
							channel = (SocketChannel) key.channel();
							//构建一个缓冲区
							ByteBuffer buffer = ByteBuffer.allocate(1024);
							//把数据写入buffer,position推进到读取的字节数数字
							int len = channel.read(buffer);
							if (len > 0){
								System.out.println("["+Thread.currentThread().getName()+"]收到响应:"
										+new String(buffer.array(),0, len));
								Thread.sleep(5000);
								channel.register(selector,SelectionKey.OP_WRITE);
							}

						}else if (key.isWritable()){
							ByteBuffer buffer = ByteBuffer.allocate(1024);
							buffer.put("你好".getBytes());
							buffer.flip();
							channel = (SocketChannel) key.channel();
							channel.write(buffer);
							channel.register(selector,SelectionKey.OP_READ);
						}

					}

				}

			}catch (Exception e){
				e.printStackTrace();
			}finally {
				if (channel != null) {
					try{
						channel.close();
					}catch(IOException e)
					{
						e.printStackTrace();
					}
				} if (selector != null) {
					try{
						selector.close();
					}catch (IOException e)
					{
						e.printStackTrace();
					}
				}
			}
		}
	}
}

NIO通讯Server端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

public class NIOServer {
	private static Selector selector;

	private static LinkedBlockingQueue<SelectionKey> requestQueue;

	private static ExecutorService threadPool;

	public static void main(String[] args){
		init();
		listen();
	}

	private static void init() {
		ServerSocketChannel serverSocketChannel = null;
		try{
			selector = Selector.open();
			serverSocketChannel = ServerSocketChannel.open();
			//将Channel设置为非阻塞的 NIO就是支持非阻塞的
			serverSocketChannel.configureBlocking(false);
			serverSocketChannel.socket().bind(new InetSocketAddress(9000),100);
			//ServerSocket,就是负责去跟各个客户端连接连接请求的
			serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
			//就是仅仅关注这个ServerSocketChannel接收到的TCP连接的请求
		}catch(IOException e){
			e.printStackTrace();
		}
		requestQueue = new LinkedBlockingQueue<SelectionKey>(500);
		threadPool = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++){
			threadPool.submit(new Worker());
		}
	}

	private static void listen(){
		while (true) {
			try {
				selector.select();
				Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
				while (keysIterator.hasNext()) {
					SelectionKey key = (SelectionKey) keysIterator.next();
					//可以认为一个SelectionKey是代表了一个请求
					keysIterator.remove();
					handleRequest(key);
				}
			}catch (Throwable t) {
				t.printStackTrace();
			}
		}
	}

	private static void handleRequest(SelectionKey key) throws IOException,ClosedChannelException{
		//后台的线程池中的线程处理下面的代码逻辑
		SocketChannel channel = null;
		try {
			//如果说这个Key是一个acceptable,也就是一个连接请求
			if (key.isAcceptable()) {
				ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
				//调用accept这个方法 就可以进行TCP三次握手了
				channel = serverSocketChannel.accept();
				//握手成功的话就可以获取到一个TCP连接好的SocketChannel
				channel.configureBlocking(false);
				channel.register(selector,SelectionKey.OP_READ);
				//仅仅关注这个READ请求,就是人家发送数据过来的请求
			}
			//如果说这个key是readable,是个发送了数据过来的话,此时需要读取客户端发送过来的数据
			else if (key.isReadable()) {
				channel = (SocketChannel) key.channel();
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				int count = channel.read(buffer);
				//通过底层的socket读取数据,写buffer中,position可能就会变成21之类的
				//你读取到了多少个字节,此时buffer的position就会变成多少
				if (count > 0) {
					//准备读取刚写入的数据,就是将limit设置为当前position,将position设置为0,丢弃mark。一般就是先写入数据,接着准备从0开始读这段数据,就可以用flip
					//position = 0,limit = 21,仅仅读取buffer中,0~21这段刚刚写入进去的数据
					buffer.flip();
					System.out.println("服务端接收请求:"+new String(buffer.array(),0, count));
					channel.register(selector,SelectionKey.OP_WRITE);
				}
			}else if (key.isWritable()){
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				buffer.put("收到".getBytes());
				buffer.flip();
				channel = (SocketChannel) key.channel();
				channel.write(buffer);
				channel.register(selector,SelectionKey.OP_READ);
			}
		}catch (Throwable t) {
			t.printStackTrace();
			if (channel != null) {
				channel.close();
			}
		}
	}

//创建一个线程任务来执行

	static class Worker implements Runnable{
		@Override
		public void run(){
			while (true){
				try{
					SelectionKey key = requestQueue.take();
					handleRequest(key);
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
		private void handleRequest(SelectionKey key) throws IOException,ClosedChannelException{
			//假设想象一下,后台有个线程池获取到了请求
			//下面的代码,都是在后台线程池的工作线程里在处理和执行
			SocketChannel channel = null;
			try {
				//如果说这个key是个acceptable,是个连接请求的话
				if (key.isAcceptable()) {
					System.out.println("["+Thread.currentThread().getName()+"]接收到连接请求");
					ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
					//调用accept方法 和客户端进行三次握手
					channel = serverSocketChannel.accept();
					System.out.println("["+Thread.currentThread().getName()+"]建立连接时获取到的channel="+ channel);
					//如果三次握手成功了之后,就可以获取到一个建立好TCP连接的SocketChannel
					//这个SocketChannel大概可以理解为,底层有一个Socket,是跟客户端进行连接的
					//你的SocketChannel就是联通到那个Socket上去,负责进行网络数据的读写的
					//设置为非阻塞的
					channel.configureBlocking(false);
					//关注的是Reade请求
					channel.register(selector,SelectionKey.OP_READ);
				}
				//如果说这个key是readable,是个发送了数据过来的话,此时需要读取客户端发送过来的数据
				else if (key.isReadable()) {
					channel = (SocketChannel) key.channel();
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					int count = channel.read(buffer);
					//通过底层的socket读取数据,写入buffer中,position可能就会变成21之类的
					//你读取到了多少个字节,此时buffer的position就会变成多少
					System.out.println("["+Thread.currentThread().getName()+"]接收到请求");
					if (count > 0) {
						buffer.flip();
						//position = 0,limit = 21,仅仅读取buffer中,0~21这段刚刚写入进去的数据
						System.out.println("服务端接收请求:" + new String(buffer.array(),0, count));
						channel.register(selector,SelectionKey.OP_WRITE);
					}
				}else if (key.isWritable()){
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					buffer.put("收到".getBytes());
					buffer.flip();
					channel = (SocketChannel) key.channel();
					channel.write(buffer);
					channel.register(selector,SelectionKey.OP_READ);
				}
			} catch (Throwable t) {
				t.printStackTrace();
				if (channel != null) {
					channel.close();
				}
			}
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值