使用NIO构建服务器端和客户端(下)

这篇博客详细介绍了如何使用NIO构建非阻塞式的服务器端和客户端。通过ServerSocketChannel开启服务器端,设置非阻塞模式并绑定端口。SocketChannel用于处理客户端连接,非阻塞的ServerSocketChannel调用accept()方法会立即返回。Selector监听通道事件,如连接、读写等。客户端初始化SocketChannel,注册连接事件,连接成功后切换为写事件。服务器端接收到数据后,处理并返回,客户端则切换到读事件接收响应。文章还提供了完整的代码实现。

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

NIO搭建服务器端-ServerSocketChannel

如果你看完了上一篇日志,相信你已经万事俱备,搞明白通道Channel、数据包装Buffer和选择器Selector。那么就来用上这些组件,搭建一个非阻塞式的,更高性能的服务器端和客户端吧!

首先来看看最核心的服务器部分:

package com.justin.nioclient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOclient implements Runnable {
	private Selector selector;
	
	protected void initClient(int port, InetAddress ipAddress) throws IOException {
		// 配置要连接的远程服务器地址和端口号
		InetSocketAddress address = new InetSocketAddress(ipAddress, port); 
		SocketChannel channel = SocketChannel.open(); //打开通道
		channel.configureBlocking(false); // 设置为非阻塞式
		channel.connect(address); // connect()方法连接远程服务器
		this.selector = Selector.open(); // 创建选择器
		channel.register(selector, SelectionKey.OP_CONNECT); // 注册连接事件
	}
	
	protected void connectServer() throws IOException {
		while (true) {
			if(selector.isOpen() == false) {
				break;
			}
			selector.select(); //查找连接事件的通道
			Set connectKeySet = selector.selectedKeys();
			Iterator iterator = connectKeySet.iterator(); //迭代器遍历通道集合
			while (iterator.hasNext()) {
				SelectionKey availableKey = (SelectionKey) iterator.next();
				iterator.remove();
				
				if(availableKey.isConnectable()) {
					System.out.println("Search connectable channel success!");
					new clientConnect().Connect(selector, availableKey);
				} else if(availableKey.isValid() && availableKey.isWritable()) {
					System.out.println("Search writable channel success!");
					new clientWrite().Write(selector, availableKey);
				} else if(availableKey.isValid() && availableKey.isReadable()) {
					System.out.println("Search readable channel ssuccess!");
					new clientRead().Read(selector, availableKey);
				}
			}
		}
	}
	@Override
	public void run() {
		System.out.println("Start client.");
		NIOclient client = new NIOclient();
		try {
			client.initClient(5000, InetAddress.getLocalHost());
			client.connectServer();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args) throws UnknownHostException, IOException {
		Thread clientThread = new Thread(new NIOclient());
		clientThread.start();
	}
}

代码第7行,打开一个ServerSocketChannel,这是一个可以监听TCP连接的通道,相当于IO中的ServerSocket,在第8行设置这个通道为非阻塞式后,第12行我们会给它绑定主机地址和端口号。最后在第14行为该通道Channel注册接收客户端连接事件,并让selector来管理这个通道,可以看到,register()的返回值是一个SelectionKey对象,上一篇日志说了,对象里面存放了通道,被管理的选择器和通道的注册事件等。

      接着就可以不断监听这个端口,等待新的客户端连接进来。和ServerSocket一样,ServerSocketChannel也有一个accept()方法,调用accept()方法后,不同的是,ServerSocket的accept()方法会一直阻塞,知道有新的连接请求,而ServerSocketChannel可以设置成非阻塞式的,上面我们已经设置了。非阻塞式的ServerScketChannel调用accept()方法,会立刻返回,如果没有新的连接请求,则返回null。

      监听到客户端的请求,之后第17行就可以调用Selector.select()方法,来返回获得那些对服务器端或客户端想要执行的事件已就绪的通道,不过要注意的是࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值