高并发基础:利用Selector实现Client与Server之间的通信

一、建立Client端

package com.nio.selector.selector;

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 Client {

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

		// 创建客户端的通道
		SocketChannel sc = SocketChannel.open();

		// 获取选择器
		Selector selc = Selector.open();

		// 选择器管理的连接要求必须是非阻塞的
		sc.configureBlocking(false);

		// 将客户端注册到选择器身上,并且申请了一个可连接事件
		sc.register(selc, SelectionKey.OP_CONNECT);

		// 发起连接
		sc.connect(new InetSocketAddress("localhost", 8090));

		// 从这儿开始的代码针对多客户端来进行操作的
		while (true) {

			// 选择出注册过的通道
			selc.select();

			// 针对通道的不同事件类型进行处理
			Set<SelectionKey> keys = selc.selectedKeys();

			Iterator<SelectionKey> it = keys.iterator();
			while (it.hasNext()) {
				// 根据事件类型进行处理
				SelectionKey key = it.next();

				// 判断是否是可连接事件
				if (key.isConnectable()) {

					// 从当前事件中获取到对应的通道
					SocketChannel scx = (SocketChannel) key.channel();

					// 如果是可连接事件,判断连接是否成功
					while (!scx.finishConnect())
						;

					// 如果连接成功了,可能会向服务器端发数据或者读数据
					scx.register(selc, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
				}

				// 判断是否是可写事件
				if (key.isWritable()) {

					// 从当前事件中获取到对应的通道
					SocketChannel scx = (SocketChannel) key.channel();

					// 写出数据
					scx.write(ByteBuffer.wrap("hello~~~".getBytes()));

					// 需要去掉可写事件
					// 获取这个通道身上的所有的事件
					scx.register(selc, key.interestOps() ^ SelectionKey.OP_WRITE);

				}

				// 判断是否是可读事件
				if (key.isReadable()) {

					// 从当前事件中来获取到对应的通道
					SocketChannel scx = (SocketChannel) key.channel();

					// 读取数据
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					scx.read(buffer);
					buffer.flip();
					System.out.println(new String(buffer.array(), 0, buffer.limit()));

					// 需要去掉这个可读事件
					scx.register(selc, key.interestOps() ^ SelectionKey.OP_READ);
				}

				// 处理完这一大类事件之后
				it.remove();
			}

		}

	}

}

二、建立Server端

package com.nio.selector.selector;

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 Server {
	
	public static void main(String[] args) throws IOException {
		
		// 创建服务器端的通道
		ServerSocketChannel ssc = ServerSocketChannel.open();
		
		// 绑定要监听的端口号
		ssc.bind(new InetSocketAddress(8090));
		
		// 开启选择器
		Selector selc = Selector.open();
		
		ssc.configureBlocking(false);
		
		// 将通道注册到选择器上,需要注册一个可接受事件
		ssc.register(selc, SelectionKey.OP_ACCEPT);
		
		while(true){
			
			// 选择出已经注册的连接
			selc.select();
			
			// 根据事件的不同进行分别的处理
			Set<SelectionKey> keys = selc.selectedKeys();
			
			Iterator<SelectionKey> it = keys.iterator();
			while (it.hasNext()) {
				// 将事件取出来分别进行处理
				SelectionKey key = it.next();
				
				// 判断可接受事件
				if(key.isAcceptable()){
					
					// 从事件中获取到对应的通道
					ServerSocketChannel sscx = (ServerSocketChannel) key.channel();
					
					// 接受连接
					SocketChannel sc = sscx.accept();
					sc.configureBlocking(false);
										
					// 注册读写事件
					sc.register(selc, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
					
				}
				
				// 判断可读事件
				if(key.isReadable()){
					
					// 从事件中获取到对应的通道
					SocketChannel sc = (SocketChannel) key.channel();
					
					// 读取数据
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					sc.read(buffer);
					buffer.flip();
					System.out.println(new String(buffer.array(),0, buffer.limit()));
					
					// 需要去掉可读事件
					sc.register(selc, key.interestOps() ^ SelectionKey.OP_READ);
					
				}
				
				// 判断可写事件
				if(key.isWritable()){
					
					// 从事件中获取到对应的通道
					SocketChannel sc = (SocketChannel) key.channel();
					
					// 写出数据
					sc.write(ByteBuffer.wrap("收到".getBytes()));
					
					// 需要去掉可写事件
					sc.register(selc, key.interestOps() ^ SelectionKey.OP_WRITE);
					
				}
				
				// 去掉这一大类的事件
				it.remove();
				
			}
			
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值