NIO完成一个客户端和服务器

NIO实现网络聊天室

1. Selector

在进入代码之前,我们先了解一下选择器:Selector,网络编程的大哥大,服务器可以执行一个线程,运行Selector程序,进行监听操作。它的作用是检查一个或者多个通道的状态是否可读。

Selector 中常用的方法:

  1. public static Selector Open();
    得到一个选择器对象
  2. public int select(long timeout);
    监听所有注册通道,如果存在IO流操作,会将对应的信息SelectionKey存入到内部的集合中,参数是一个超时时间
  3. public Set selectionKeys();
    返回当前Selector内部集合中保存的所有SelectionKey
1.1 SelectionKey

SelectionKey 表示 SelectionKey 和网络通道直接的关系
int OP_ACCEPT; 16 需要连接
int OP_CONNECT; 8 已经连接
Int OP_WRITE; 4 写入操作
int OP_READ; 1 读取操作

方法:

  1. public abstract Selector selector();
    得到与之关联的 Selector 对象
  2. public abstract SelectableChannel channel();
    得到与之关联的通道
  3. public final Object attachment();
    得到与之关联的共享数据
  4. public abstract SelectionKey interestOps(int ops);
    设置或改变监听事件
  5. public final boolean isAcceptable();
    是否可以 accept
  6. public final boolean isReadable();
    是否可以读
  7. public final boolean isWritable();
    是否可以写
1.2 ServerSocketChannel

服务端Socket程序对应的Channel通道

方法 作用
public static ServerSocketChannel open() 开启服务器ServerSocketChannel通道
public final ServerSocketChannel bind(SocketAddress local); 设置服务器端端口号
public final SelectableChannel configureBlocking(boolean block); 设置阻塞或非阻塞模式,false 表示非阻塞
public SocketChannel accept() 获取一个客户端连接,并且得到对应的操作通道
public final SelectionKey register(Selector sel, int ops) 注册当前选择器,并且选择监听什么事件
1.3 SocketChannel

客户端Socket对应的Channel对象

方法 作用
static SocketChannel open() 打开一个Socket客户端Channel对象
final SelectableChannel configureBlocking(boolean block) 设置是阻塞状态,还是非阻塞状态
boolean connect(SocketAddress remote) 连接服务器
boolean finishConnect() 如果connect连接失败,可以通过finishConnect继续连接
final SelectionKey register(Selector sel, int ops, Object attechment); 注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attachment参数
final void close() 关闭SocketChannel

2. NIO 完成一个客户端和服务器

2.1 服务器部分
public static void main(String[] args) throws IOException {
	// 1. 开启服务器
	ServerSocketChannel serverSocket = ServerSocketChannel.open();

	// 2. 开启Selector
	Selector selector = Selector.open();

	// 3. 服务端代码绑定端口号
	serverSocket.bind(new InetSocketAddress(8848));
	
	// 4. 设置非阻塞状态
	serverSocket.configureBlocking(false);
	
	//5. ServerSocketChannel 注册--> Selector 返回值是一个SelectionKey
	serverSocket.register(selector, SelectionKey.OP_ACCEPT);
	
	while (true) {

    		if (0 == selector.select(1000)) {
        			// 0 == selector.select(1000) 表示没有连接到客户端
        			continue;
    		}
    		Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
		while (selectionKeys.hasNext()) {
			SelectionKey selectionKey = selectionKeys.next();
			//连接请求
			if (selectionKey.isAcceptable()) {
    				System.out.println("客户端请求连接!!!");		
   				SocketChannel socket = serverSocket.accept();
    				socket.configureBlocking(false);
    				socket.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024 * 4));
  			}
  			//可读
  			if (selectionKey.isReadable()) {
    				SocketChannel socket = (SocketChannel) selectionKey.channel();
    				ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
    				socket.read(buffer);
    				System.out.println("客户端发送数据:" + new String(buffer.array()));
			}
			selectionKeys.remove();
		}
	}
}
2.2 客户端部分
//main方法内
SocketChannel socket = SocketChannel.open();
socket.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress("192.168.1.110", 8848);
//l连接服务器
if (!socket.connect(address)) {
    while (!socket.finishConnect()) {
        System.out.println("保持呼叫服务器状态,但是我还能做点别的事情~~~ 等待2s继续申请连接~~~");
        Thread.sleep(2000);
    }
}

//准备一个数据存入到缓冲区
ByteBuffer buffer = ByteBuffer.wrap("你好,服务器,我在等你...".getBytes());
socket.write(buffer);

new Scanner(System.in).nextLine();
发布了26 篇原创文章 · 获赞 28 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43932553/article/details/104907032