服务端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class ChatServer {
public void serverDemo() throws IOException {
// 创建Selector选择器
Selector selector = Selector.open();
// 创建ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8000));
// 创建ByteBuffer缓存区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 设置非阻塞IO
serverSocketChannel.configureBlocking(false);
// 将serverSocketChannel通道注册到Selector选择器中
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("——————————服务器启动成功!——————————");
// 遍历可用连接
for (;;){
// 查看有多少连接
int select = selector.select();
// 若是没有连接跳过循环
if (select ==0){
continue;
}
// 获取可用的连接集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 遍历连接集合
Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
while (selectionKeyIterator.hasNext()){
// 从集合中获取单个连接
SelectionKey selectionKey = selectionKeyIterator.next();
// 删除集合
selectionKeyIterator.remove();
// 判断该链接的状态
// 如果是accept状态的
if (selectionKey.isAcceptable()){
acceptOperator(serverSocketChannel,selector);
}
// 如果是只读状态的
if (selectionKey.isReadable()){
readOperator(selector,selectionKey);
}
}
}
}
// 读取客户端发来的消息
private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
// 创建socketChannel通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 切换非阻塞状态
socketChannel.configureBlocking(false);
// 将通道数据写入到缓存中
int read = socketChannel.read(buffer);
// 遍历输出客户端发来的消息
String message = "";
if (read !=-1){
buffer.flip();
message+= Charset.forName("UTF-8").decode(buffer);
}
// 将通道注册到selector选择器中
socketChannel.register(selector,SelectionKey.OP_READ);
// 广播给其他客户端
if (message.length()>0){
System.out.print(message);
castOtherClient(message,selector,socketChannel);
}
}
// 广播客户端发来的信息,发给每个客户端
private void castOtherClient(String message, Selector selector, SocketChannel socketChannel) throws IOException {
// 获取所有接入的信息
Set<SelectionKey> keys = selector.keys();
// 循环获取每个channel,广播信息
for (SelectionKey key : keys) {
// 获取每个channel
Channel channel = key.channel();
if (channel instanceof SocketChannel && channel != socketChannel){
// 给每个channel发送信息
((SocketChannel) channel).write(Charset.forName("UTF-8").encode(message));
}
}
}
//
private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
// 接入状态,创建socketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
// 把socketChannel通道,设置成非阻塞模式
socketChannel.configureBlocking(false);
// 把socketChannel注册到选择器中
socketChannel.register(selector,SelectionKey.OP_READ);
// 给客户端回复信息
socketChannel.write(Charset.forName("UTF-8").encode("您已进入聊天室,请注意你的隐私,防止隐私泄露"+"\n"));
}
public static void main(String[] args) throws IOException {
new ChatServer().serverDemo();
}
}
客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
public class ChatClient {
public void chatClient(String name) throws IOException {
// 创建SocketChannel
SocketChannel socketChannel = SocketChannel
.open(new InetSocketAddress("127.0.0.1",8000));
// 创建选择器
Selector selector = Selector.open();
// 切换非阻塞模式
socketChannel.configureBlocking(false);
// 将通道注册进选择器
socketChannel.register(selector, SelectionKey.OP_READ);
// 创建线程,返回其他客户端的消息
new Thread(new ChatThread(selector)).start();
// 获取控制台的信息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String s = scanner.nextLine();
if (s.length()>0){
socketChannel.write(Charset.forName("UTF-8").encode(name+" : "+s+"\n"));
}
}
}
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class ChatThread implements Runnable{
private Selector selector;
public ChatThread(Selector selector){
this.selector=selector;
}
@Override
public void run() {
try{
// 遍历可用连接
for (;;){
// 查看有多少连接
int select = selector.select();
// 若是没有连接跳过循环
if (select ==0){
continue;
}
// 获取可用的连接集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 遍历连接集合
Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
while (selectionKeyIterator.hasNext()){
// 从集合中获取单个连接
SelectionKey selectionKey = selectionKeyIterator.next();
// 删除集合
selectionKeyIterator.remove();
// 判断该链接的状态
// 如果是只读状态的
if (selectionKey.isReadable()){
readOperator(selector,selectionKey);
}
}
}
}catch (Exception e){
}
}
private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
// 创建socketChannel通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 切换非阻塞状态
socketChannel.configureBlocking(false);
// 将通道数据写入到缓存中
int read = socketChannel.read(buffer);
// 遍历输出客户端发来的消息
String message = "";
if (read !=-1){
buffer.flip();
message+= Charset.forName("UTF-8").decode(buffer);
}
// 将通道注册到selector选择器中
socketChannel.register(selector,SelectionKey.OP_READ);
// 广播给其他客户端
if (message.length()>0){
System.out.print(message);
}
}
}
下面模拟创建3个用户(A,B,C)进行聊天
import java.io.IOException;
public class AClient {
public static void main(String[] args) throws IOException {
new ChatClient().chatClient("A");
}
}
import java.io.IOException;
public class BClient {
public static void main(String[] args) throws IOException {
new ChatClient().chatClient("B");
}
}
import java.io.IOException;
public class CClient {
public static void main(String[] args) throws IOException {
new ChatClient().chatClient("C");
}
}
测试
顺序:
- 先启动服务端
- 在启动三个客户端
假如在A客户输入一句话,回车;可以看到B,C,服务端同样接收到了A发送的信息,同理,B,C发消息一样可以被其他用户和服务端看到。
A用户
B 用户
服务端