NIO server client

服务端

package com.suosuo.myprojects.netty.channel;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.util.Iterator;
import java.util.Set;

/**
 * 1. 监听
 * 1.1 服务端通过 SelectionKey 监听客户端连接事件
 * 1.2 SelectionKey 监听客户端 SocketChannel 数据读取事件
 * 2. 关系 Selector selectionkey = channel = buffer 1:n:n:n
 * <p>
 * 总结:
 * 创建服务端ServerSocketChannel,并监听端口
 * 创建Selector
 * 服务端ServerSocketChannel 注册到Selector,监听客户端连接事件
 * Selector 监听客户端连接事件,存在连接,获取该Selector 对应所有Selectionkey
 * 遍历Selctionkey,监听连接事件,针对每个客户端判断是否有连接请求
 * 创建客户端socketChannel,并注册到Selector,监听客户端读事件
 * 读取客户端数据,从channel中读取到ByteBuffer
 */
@Slf4j
public class NIOServer {

    public static void main(String[] args) {
	ServerSocketChannel serverSocketChannel = null;
	try {
	    // 创建ServerSocketChannel
	    serverSocketChannel = ServerSocketChannel.open();

	    // 服务端创建serverSocket 监听 端口
	    ServerSocket socket = serverSocketChannel.socket();
	    socket.bind(new InetSocketAddress(6666));

	    // 设置为非阻塞
	    serverSocketChannel.configureBlocking(false);

	    // 创建selector
	    Selector selector = Selector.open();

	    // 把serverSocketChannel注册到selector 关心 事件为客户端连接事件
	    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

	    // 缓存等待客户端连接
	    while (true) {
		// selector 进行监听 select方法,
		// 等待1s,没有连接事件发生,返回,这里需要注意Selector监听很多个客户端,这里可能连接多个,对应多个通道,下面获取SelectionKey才有意义
		if (selector.select(1000) == 0) {
		    log.info("服务器等待了1s,无连接");
		    continue;
		}

		// 如果返回>0,获取selectionkey集合,返回有事件发生的通道个数
		Set<SelectionKey> selectionKeys = selector.selectedKeys();

		// 遍历selectionkey
		Iterator<SelectionKey> iterator = selectionKeys.iterator();
		if (iterator.hasNext()) {
		    // 获取key,对应一个通道
		    SelectionKey key = iterator.next();
		    // 判断key 对应通道发生的事件,如果是op_accept,有新的客户的连接
		    if (key.isAcceptable()) {
			// 为该客户端生成一个socketChannel
			SocketChannel socketChannel = serverSocketChannel.accept();
			log.info("服务端:客户端连接成功,生成了一个socketChannel:{}", socketChannel.hashCode());
			// 设置为非阻塞
			socketChannel.configureBlocking(false);
			// 将 socketChannel 注册到Selector上,一个Selector上可以注册多个SocketChannel
			// 关注客户端读事件,并关联一个buffer,读取通道的中数据到buffer中, 由此SocketChannel 与 Selector建立关联
			socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
		    }
		    // 判断客户端是否有读事件发生,根据selectkey判断
		    if (key.isReadable()) {
			log.info("服务端:监听到客户端请求,开始读取客户端通道数据...");
			// 通过selectionkey 反向获取SocketChanel
			//		    SelectableChannel selectableChannel = key.channel();
			SocketChannel socketChannel = (SocketChannel) key.channel();
			// 获取channel关联buffer
			ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
			// 读取客户端通道中数据写入buffer
			socketChannel.read(byteBuffer);
			log.info("服务端: 读取客户端数据:{}", new String(byteBuffer.array()));
		    }
		    // SelectionKey 处理完删除,避免重复操作
		    iterator.remove();
		}
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    try {
		serverSocketChannel.close();
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	}
    }
}

客户端

package com.suosuo.myprojects.netty.channel;

import lombok.extern.slf4j.Slf4j;

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

/**
 * 1.创建客户端channel
 * 2.监听服务端,绑定端口
 * 连接服务器
 * 3.创建buffer,写入发送内容
 * 4.写入channel
 */
@Slf4j
public class NIOClient {

    public static void main(String[] args) throws IOException {

	SocketChannel socketChannel = SocketChannel.open();

	InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);

	// 客户端连接服务端
	if (!socketChannel.connect(inetSocketAddress)) {
	    // 没连接成功,可能是没连接完成
	    if (!socketChannel.finishConnect()) {
		log.info("连接需要时间,客户端不会阻塞,可以做其他工作");
	    }
	}

	String content = "hello world!";

	ByteBuffer byteBuffer = ByteBuffer.wrap(content.getBytes());

	socketChannel.write(byteBuffer);
	log.info("客户端:数据已写入通道,已发送完成");

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现一个和Netty一样的功能Server端和client端,可以使用Java NIO来进行实现。下面是一个简单的示例代码: ### Server端 ```java 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.util.Iterator; import java.util.Set; public class NioServer { private Selector selector; private ByteBuffer readBuffer = ByteBuffer.allocate(1024); private ByteBuffer writeBuffer = ByteBuffer.allocate(1024); public NioServer(int port) { try { // 创建ServerSocketChannel对象并绑定端口 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(port)); serverSocketChannel.configureBlocking(false); // 创建Selector对象 selector = Selector.open(); // 将ServerSocketChannel注册到Selector上,并设置为监听OP_ACCEPT事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server started, listening on port " + port); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public void start() { try { while (true) { // 阻塞等待事件的发生 selector.select(); // 获取发生事件的SelectionKey集合 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); if (selectionKey.isAcceptable()) { // ServerSocketChannel可以接收客户端连接 ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel(); SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); System.out.println("Client " + socketChannel.getRemoteAddress() + " connected."); } else if (selectionKey.isReadable()) { // SocketChannel可以读取数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); readBuffer.clear(); int numRead = socketChannel.read(readBuffer); if (numRead == -1) { // 客户端关闭连接 selectionKey.cancel(); socketChannel.close(); System.out.println("Client " + socketChannel.getRemoteAddress() + " disconnected."); } else { // 处理读取到的数据 String request = new String(readBuffer.array(), 0, numRead); System.out.println("Received request from client " + socketChannel.getRemoteAddress() + ": " + request); socketChannel.register(selector, SelectionKey.OP_WRITE); } } else if (selectionKey.isWritable()) { // SocketChannel可以写入数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); writeBuffer.clear(); String response = "Hello from server!"; writeBuffer.put(response.getBytes()); writeBuffer.flip(); socketChannel.write(writeBuffer); socketChannel.register(selector, SelectionKey.OP_READ); } } } } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public static void main(String[] args) { NioServer server = new NioServer(8888); server.start(); } } ``` ### Client端 ```java 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; import java.util.Set; public class NioClient { private Selector selector; private ByteBuffer readBuffer = ByteBuffer.allocate(1024); private ByteBuffer writeBuffer = ByteBuffer.allocate(1024); public NioClient(String host, int port) { try { // 创建SocketChannel对象并连接服务器 SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress(host, port)); // 创建Selector对象 selector = Selector.open(); // 将SocketChannel注册到Selector上,并设置为监听OP_CONNECT事件 socketChannel.register(selector, SelectionKey.OP_CONNECT); System.out.println("Connecting to server " + host + ":" + port); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public void start() { try { while (true) { // 阻塞等待事件的发生 selector.select(); // 获取发生事件的SelectionKey集合 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); if (selectionKey.isConnectable()) { // SocketChannel已连接到服务器 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); if (socketChannel.isConnectionPending()) { socketChannel.finishConnect(); } socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_WRITE); System.out.println("Connected to server " + socketChannel.getRemoteAddress()); } else if (selectionKey.isReadable()) { // SocketChannel可以读取数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); readBuffer.clear(); int numRead = socketChannel.read(readBuffer); if (numRead == -1) { // 服务器关闭连接 selectionKey.cancel(); socketChannel.close(); System.out.println("Server " + socketChannel.getRemoteAddress() + " disconnected."); } else { // 处理读取到的数据 String response = new String(readBuffer.array(), 0, numRead); System.out.println("Received response from server " + socketChannel.getRemoteAddress() + ": " + response); socketChannel.register(selector, SelectionKey.OP_WRITE); } } else if (selectionKey.isWritable()) { // SocketChannel可以写入数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); writeBuffer.clear(); String request = "Hello from client!"; writeBuffer.put(request.getBytes()); writeBuffer.flip(); socketChannel.write(writeBuffer); socketChannel.register(selector, SelectionKey.OP_READ); } } } } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public static void main(String[] args) { NioClient client = new NioClient("localhost", 8888); client.start(); } } ``` 这个示例代码实现了一个简单的NIO ServerClient,可以接收客户端连接,读取客户端发送的数据,并回复一条消息。虽然它没有Netty那么强大,但是可以作为一个参考来了解Java NIO的基本原理和使用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值