package com.my.io.chat.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
/**
* @author zhupanlin
* @version 1.0
* @description: 聊天室的服务端
* @date 2024/1/27 9:55
*/
public class ChatServer {
public static void main(String[] args) {
try {
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(9001));
// 得到selector
Selector selector = Selector.open();
// 将channel注册到selector上,并让selector对其接收的就绪状态感兴趣
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("聊天室服务器启动成功");
// 轮询处理
while (true){
// 获得处于就绪状态的channel的总数
int select = selector.select();
if (select == 0){
continue;
}
// 获得就绪状态所有的selectionKeys集合
Set selectionKeys = selector.selectedKeys();
// 遍历selectionKeys集合
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 判断channel哪个操作处于就绪状态 channel被封装在selectionKey中
if (selectionKey.isAcceptable()){
// 说明有客户端连接,处理客户端的连接
handleAccept(selectionKey);
}else if (selectionKey.isReadable()){
// 说明有客户端向服务端写数据,处理客户端的写请求
handleRead(selectionKey);
}
//删除当前key
iterator.remove();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 处理客户端的连接请求
* @param selectionKey
*/
private static void handleAccept(SelectionKey selectionKey) {
try {
// 得到ServerSocketChannel
ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
// 获得selector
Selector selector = selectionKey.selector();
// 建立连接,获得socketChannel 此socketChannel与客户端的socketChannel为同一个
SocketChannel socketChannel = channel.accept();
// 将socketChannel设置成非阻塞
socketChannel.configureBlocking(false);
// 注册到selector中,让Selector对它的读操作感兴趣
socketChannel.register(selector, SelectionKey.OP_READ);
// 服务端向客户端回复一条消息
String message = "欢迎进入聊天室";
socketChannel.write(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 处理客户端写数据的请求--服务端处于读操作的就绪状态
* 获得客户端写的数据,并且广播给其它所有的客户端
* @param selectionKey
*/
private static void handleRead(SelectionKey selectionKey) {
try {
// 获得SocketChannel
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 获得selector
Selector selector = selectionKey.selector();
// 创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据到buffer中
int len = 0;
// 存放客户端发来的消息
String message = "";
while ((len = socketChannel.read(buffer)) > 0){
buffer.flip();
// 封装message
message += new String(buffer.array(), 0, len, StandardCharsets.UTF_8);
}
// 把客户端发送的消息,广播给其它所有客户端
if (message.length() > 0){
// 服务端本地打印
System.out.println(socketChannel.getRemoteAddress() + ":" + message);
// 广播消息
handleMessage(message, selectionKey);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 广播消息给其它所有客户端
* @param message
* @param selectionKey
*/
private static void handleMessage(String message, SelectionKey selectionKey) {
try {
// 获得selector
Selector selector = selectionKey.selector();
// 获得selector上面所有注册的channel
Set keys = selector.keys();
// 遍历
for (SelectionKey key : keys) {
SelectableChannel channel = key.channel();
// 不发给自己
if (channel != selectionKey.channel() && channel instanceof SocketChannel){
// 发送消息
// 获得SocketChannel
SocketChannel socketChannel = (SocketChannel) channel;
// 发送消息
socketChannel.write(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2.聊天室客户端编写
package com.my.io.chat.client;
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.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
* @author zhupanlin
* @version 1.0
* @description: 客户端程序
* 1. 发送数据到服务端
* 2. 接收服务端发来的消息
* @date 2024/1/27 11:07
*/
public class ChatClient {
/**
* 启动客户端的方法
* @param name 聊天室昵称
*/
public void start(String name){
try {
// 创建channel
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9001));
// 设置成非阻塞
socketChannel.configureBlocking(false);
// 获得selector
Selector selector = Selector.open();
// 注册channel到selector上,让selector对其读操作感兴趣,读取服务端发来的消息
socketChannel.register(selector, SelectionKey.OP_READ);
// 创建线程 专门负责发送消息
new SendMessageThread(socketChannel, name).start();
// 循环selector
while (true){
// 获得这次就绪状态的channel数量
int select = selector.select();
if (select == 0){
continue;
}
// 获得所有就绪状态的channel
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
// 遍历
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isReadable()){
// 如果是读操作就绪,说明服务端广播数据了
// 获得channel
SocketChannel channel = (SocketChannel) key.channel();
// 创建buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取
int len = 0;
String message = "";
while ((len = channel.read(buffer)) > 0){
buffer.flip();
// 封装message
message += new String(buffer.array(), 0, len, StandardCharsets.UTF_8);
buffer.clear();
}
// 打印消息
System.out.println(message);
// 将channel再次注册到selector上,并让selector对读感兴趣
channel.register(selector, SelectionKey.OP_READ);
}
// 移除key
iterator.remove();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
class SendMessageThread extends Thread{
// 要发消息的socketChaneel
private SocketChannel socketChannel;
//自己的昵称
private String name;
public SendMessageThread(SocketChannel socketChannel, String name) {
this.socketChannel = socketChannel;
this.name = name;
}
@Override
public void run() {
// 控制台输入
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String message = sc.nextLine();
// 带上名字
String sendMessage = name + ":" + message;
// 发消息到channel
try {
if (message.length() > 0){
socketChannel.write(ByteBuffer.wrap(sendMessage.getBytes(StandardCharsets.UTF_8)));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}