NIO创建server和client

server


public class NioChatServer {
	// 端口
	private int port;
	// 设置缓冲区大小,1024字节
	private final int BUFFER = 1024;
	// 读缓冲区 写缓冲区
	private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER);
	private ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER);
	// 编码方式设置为utf-8,下面字符和字符串互转时用得到
	private Charset charset = Charset.forName("UTF-8");

	// 设置端口
	public NioChatServer(int port) {
		this.port = port;
	}

	private void start() throws IOException {
		// 创建ServerSocketChannel和Selector并打开
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		Selector selector = Selector.open();
		// 设置非阻塞
		serverSocketChannel.configureBlocking(false);
		// 绑定监听端口,这里不是给ServerSocketChannel绑定,而是给ServerSocket绑定,socket()就是获取通道原生的ServerSocket或Socket
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		// 把server注册到Selector并监听Accept事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("启动服务器,监听端口:" + port);
		while (true) {
			// select()会返回此时触发了多少个Selector监听的事件
			if (selector.select() > 0) {
				// 获取这些已经触发的事件,selectedKeys()返回的是触发事件的所有信息
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				for (SelectionKey selectionKey : selectionKeys) {
					handles(selectionKey, selector);
				}
				// 清空 避免重复
				selectionKeys.clear();
			}
		}

	}

	// 处理事件
	private void handles(SelectionKey selectionKey, Selector selector) throws IOException {
		// 当触发了Accept事件,也就是有客户端请求进来
		if (selectionKey.isAcceptable()) {
			// 获取ServerSocketChannel
			ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
			// 然后通过accept()方法接收客户端的请求,这个方法会返回客户端的SocketChannel,这一步和原生的ServerSocket类似
			SocketChannel client = serverSocketChannel.accept();
			client.configureBlocking(false);
			// 把客户端的SocketChannel注册到Selector,并监听Read事件
			client.register(selector, SelectionKey.OP_READ);
			System.out.println("客户端[" + client.socket().getPort() + "]上线啦!");
		}
		// 当触发了Read事件,也就是客户端发来了消息
		if (selectionKey.isReadable()) {
			// 获取客户端的SocketChannel
			SocketChannel client = (SocketChannel) selectionKey.channel();
			String msg = receive(client);
			System.out.println("客户端[" + client.socket().getPort() + "]:" + msg);
			// 把消息转发给其他客户端
			sendMessage(client, msg, selector);
			// 判断用户是否退出
			if (msg.equals("quit")) {
				// 解除该事件的监听
				selectionKey.cancel();
				// 更新Selector
				selector.wakeup();
				System.out.println("客户端[" + client.socket().getPort() + "]下线了!");
			}
		}
	}

	// 转发消息的方法
	private void sendMessage(SocketChannel client, String msg, Selector selector) throws IOException {
		msg = "客户端[" + client.socket().getPort() + "]:" + msg;
		System.out.println(selector.keys().size());
		for (SelectionKey selectionKey : selector.keys()) {
			System.out.println(selectionKey.channel().toString());
			if (!(selectionKey.channel() instanceof ServerSocketChannel) && !client.equals(selectionKey.channel())
					&& selectionKey.isValid()) {
				SocketChannel otherClient = (SocketChannel) selectionKey.channel();
				writeBuffer.clear();
				writeBuffer.put(charset.encode(msg));
				writeBuffer.flip();
				// 把消息写入到缓冲区后,再把缓冲区的内容写到客户端对应的通道中
				while (writeBuffer.hasRemaining()) {
					otherClient.write(writeBuffer);
				}
			}
		}
	}

	private String receive(SocketChannel client) throws IOException {
		// 用缓冲区之前先清空一下,避免之前的信息残留
		readBuffer.clear();
		// 把通道里的信息读取到缓冲区,用while循环一直读取,直到读完所有消息。因为没有明确的类似\n这样的结尾,所以要一直读到没有字节为止
		while (client.read(readBuffer) > 0)
			// 把消息读取到缓冲区后,需要转换buffer的读写状态,不明白的看看前面的Buffer的讲解
			readBuffer.flip();
		return String.valueOf(charset.decode(readBuffer));
	}
	public static void main(String[] args) {
		try {
			new NioChatServer(8888).start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

client

public class NioChatClient {
	private static final String ADDR = "127.0.0.1";
	private static final int PORT = 8888;
	private static final int BUFFER = 1024;
	private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER);
	private ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER);

	private SocketChannel client;
	private Selector selector;
	private Charset charset = Charset.forName("UTF-8");

	private void start() throws IOException {
		client = SocketChannel.open();
		selector = Selector.open();
		client.configureBlocking(false);
		// 注册channel,并监听SocketChannel的Connect事件
		client.register(selector, SelectionKey.OP_CONNECT);
		// 请求服务器建立连接
		client.connect(new InetSocketAddress(ADDR, PORT));
		while (true) {
			int status = selector.select();
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			for (SelectionKey selectionKey : selectedKeys) {
				handle(selectionKey);
			}
		}
	}

	private void handle(SelectionKey selectionKey) throws IOException {
		// 判断连接
		if (selectionKey.isConnectable()) {
			SocketChannel client = (SocketChannel) selectionKey.channel();
			// finishConnect()返回true,说明和服务器已经建立连接。如果是false,说明还在连接中,还没完全连接完成
			if (client.finishConnect()) {
				// 新建一个新线程去等待用户输入
				new Thread(new UserInputHandler(this)).start();
			}
			// 连接建立完成后,注册read事件,开始监听服务器转发的消息
			client.register(selector, SelectionKey.OP_READ);
		}
		// 当触发read事件,也就是获取到服务器的转发消息
		if (selectionKey.isReadable()) {
			SocketChannel client = (SocketChannel) selectionKey.channel();
			// 获取消息
			String msg = receive(client);
			System.out.println(msg);
			// 判断用户是否退出
			if (msg.equals("quit")) {
				// 解除该事件的监听
				selectionKey.cancel();
				// 更新Selector
				selector.wakeup();
			}
		}
	}

	private String receive(SocketChannel client) throws IOException {
		readBuffer.clear();
		while (client.read(readBuffer) > 0)
			readBuffer.flip();
		return String.valueOf(charset.decode(readBuffer));
	}

	public void send(String msg) throws IOException {
		if (!msg.isEmpty()) {
			writeBuffer.clear();
			writeBuffer.put(charset.encode(msg));
			writeBuffer.flip();
			while (writeBuffer.hasRemaining()) {
				client.write(writeBuffer);
			}
			if (msg.equals("quit")) {
				selector.close();
			}
		}
	}
	public static void main(String[] args) {
		try {
			new NioChatClient().start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

UserInputHandler


public class UserInputHandler implements Runnable {
	NioChatClient client;

	public UserInputHandler(NioChatClient chatClient) {
		this.client = chatClient;
	}

	@Override
	public void run() {
		BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
		while (true) {
			try {
				String input = read.readLine();
				client.send(input);
				if (input.equals("quit"))
					break;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值