NIO实现的服务端与客户端之间的简单通信

1 服务端启动类

public class TimeServer {
	public static void main(String[] args) {
		int port = 8080;
		//这个类负责轮询多路复用器 selector
		MultiplexerTimerServer mts = new MultiplexerTimerServer(port);
		//单独开启一个线程来执行mts服务
		new Thread(mts,"NIO-MultiplexerTimerServer-001").start();
	}
}

2:服务端请求处理类

public class MultiplexerTimerServer implements Runnable{

	private Selector selector;

	private ServerSocketChannel serverSocketChannel;

	private volatile boolean stop;

	public MultiplexerTimerServer(int port) {
		try {
			//创建多路复用器 selector
			selector = Selector.open();
			serverSocketChannel = ServerSocketChannel.open();
			//将serverSocketChannel设置为非阻塞模式
			serverSocketChannel.configureBlocking(false);
			//serverSocketChannel的socket绑定服务端口 并设置TCP参数
			serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
			//将serverSocketChannel 注册到selector上,并让selector监听SelectionKey 的OP_ACCEPT操作位
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("============MultiplexerTimerServer is start in port "+port);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	/**
	 * 关闭MultiplexerTimerServer服务
	 */
	public void stop() {
		stop = true;
	}

	@Override
	public void run() {
		while (!stop) {
			try { 
				//休眠1秒  无论是否有读写事件发生 selector每隔1秒被唤醒
				selector.select(1000);
				//获取注册在selector上的所有的就绪状态的serverSocketChannel中发生的事件
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterators = selectedKeys.iterator();
				SelectionKey key = null;
				while (iterators.hasNext()) {
					key = iterators.next();
					iterators.remove();
					try {
						handleInput(key);
					} catch (Exception e) {
						if (key != null) {
							key.cancel();
							if (key.channel() != null) {
								key.channel().close();
							}
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		if (selector != null) { //服务停止 关闭selector
			try {
				selector.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 处理客户端新接入的请求
	 * @param key 请求信息
	 * @throws IOException
	 */
	private void handleInput(SelectionKey key) throws IOException{
		if (key.isValid()) {
			//处理新接入的请求消息
			if (key.isAcceptable()) {
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				sc.register(selector,SelectionKey.OP_READ);
			}

			if (key.isReadable()) {
				//获取SocketChannel实例后,相当于完成了TCP三次握手,TCP物理链路正式建立
				SocketChannel sc = (SocketChannel) key.channel();
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				//读取客户端传过来的请求参数
				int readBytes = sc.read(readBuffer);

				if (readBytes > 0) {//获取到了请求数据,对字节进行编解码
					readBuffer.flip();
					byte[] bytes = new byte[readBuffer.remaining()];
					readBuffer.get(bytes);

					String requestMessage = new String(bytes, Charset.forName("UTF-8"));
					System.out.println("============MultiplexerTimerServer receive message:"+requestMessage);
					doWrite(sc,"responseMessage");
				} else if (readBytes < 0) {//没有获取到请求数据,需要关闭SocketChannel(C/S 连接链路)
					key.cancel();
					sc.close();
				}
			}
		}
	}

	/**
	 * 将请求的响应 异步返回给客户端
	 * @param sc (C/S 连接链路)
	 * @param response 响应数据
	 * @throws IOException
	 */
	private void doWrite(SocketChannel sc, String response) throws IOException {
		if (response != null && !response.isEmpty()) {
			byte[] bytes = response.getBytes();
			ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
			buffer.put(bytes);
			buffer.flip();
			sc.write(buffer);
		}
	}

}

3:客户端启动类

public class TimeClient {
	public static void main(String[] args) {
		//服务器端的端口
		int port = 8080;
		//这个类负责轮询多路复用器 selector
		TimeClientHandle tch = new TimeClientHandle("127.0.0.1",port);
		//单独开启一个线程来执行tch服务
		new Thread(tch,"NIO-TimeClient-001").start();
	}
}

4:客户端实现类

public class TimeClientHandle implements Runnable {

	private String ip;

	private int port;

	private Selector selector;

	private SocketChannel socketChannel;

	private volatile boolean stop;

	public TimeClientHandle (String ip,int port) {
		this.ip = ip == null ? "127.0.0.1" : ip;
		this.port = port;

		try {
			selector = Selector.open();
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	@Override
	public void run() {

		try {
			doConnect(); //请求建立连接
		} catch (IOException e) {
			e.printStackTrace();
		}

		while (!stop) {

			try {
				//休眠1秒  无论是否有读写事件发生 selector每隔1秒被唤醒
				selector.select(1000);
				//获取注册在selector上的所有的就绪状态的serverSocketChannel中发生的事件
				Set<SelectionKey> set = selector.selectedKeys();
				Iterator<SelectionKey> it = set.iterator();
				SelectionKey key = null;
				while (it.hasNext()) {
					key = it.next();
					it.remove();

					try {
						handleInput(key);
					} catch (Exception e) {
						if (key != null) {
							key.cancel();
							if (key.channel() != null) {
								key.channel().close();
							}
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

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

	private void handleInput(SelectionKey key) throws IOException {
		if (key.isValid()) {
			SocketChannel sc = (SocketChannel) key.channel();
			if (key.isConnectable()) { //处于连接状态
				if (sc.finishConnect()) {//客户端连接成功
					sc.register(selector, SelectionKey.OP_READ);
					doWrite(sc);
				} else { //连接失败
					System.exit(1);
				}
			}

			if (key.isReadable()) {//如果客户端接收到了服务器端发送的应答消息 则SocketChannel是可读的
				ByteBuffer bf = ByteBuffer.allocate(1024);
				int bytes = sc.read(bf);
				if (bytes > 0) {
					bf.flip();
					byte[] byteArray = new byte[bf.remaining()];
					bf.get(byteArray);
					String resopnseMessage = new String(byteArray, "UTF-8");
					System.out.println("=======The response message is:"+resopnseMessage);
					this.stop = true;
				} else if (bytes < 0) {
					key.cancel();
					sc.close();
				}
			}
		}
	}

	private void doConnect() throws IOException {
		//如果直连接连接成功,则注册到多路复用器上,并注册SelectionKey.OP_READ操作
		if (socketChannel.connect(new InetSocketAddress(ip, port))) {
			socketChannel.register(selector, SelectionKey.OP_READ);
			//发送请求消息 读应答
			doWrite(socketChannel);
		} else {//如果直连接连接未成功,则注册到多路复用器上,并注册SelectionKey.OP_CONNECT操作
			socketChannel.register(selector, SelectionKey.OP_CONNECT);
		} 
	}

	private void doWrite(SocketChannel sc) throws IOException {

		byte[] requestBytes = "request message from client".getBytes();
		ByteBuffer bf = ByteBuffer.allocate(requestBytes.length);
		bf.put(requestBytes);
		bf.flip();
		sc.write(bf);

		if (!bf.hasRemaining()) {//如果缓冲区里面的所有内容全部发送完毕
			System.out.println("=======client send requst message to server successed!");
		}
	}
}



猜你喜欢

转载自blog.csdn.net/qq_34310242/article/details/79779962