摘要
服务器端
该功能主要是实现了一个简单的群聊的功能,后期我将深入的对代码进行研究和改进,最后将实现模拟微信功能的多人聊天系统,同时也将梳理好项目的设计的流程和原理图,提供大家参考和学习。
功能原理图
package com.zhuangxiaoyan.nio.groupchat;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
* @Classname GroupChatServer
* @Description TODO
* @Date 2021/11/4 22:40
* @Created by xjl
*/
public class GroupChatServer {
//定义属性
private Selector selector;
private ServerSocketChannel listenerChannel;
private static final int PORT = 6667;
//构造器
//初始化工作
public GroupChatServer() {
try {
//获取选择器
selector = Selector.open();
//SeverSockerChannel
listenerChannel = ServerSocketChannel.open();
//绑定端口
listenerChannel.socket().bind(new InetSocketAddress(PORT));
//设置非阻塞模式
listenerChannel.configureBlocking(false);
//将该listenChannel 注册到selector
listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
e.printStackTrace();
}
}
//监听程序
public void listen() {
try {
//循环处理
while (true) {
int count = selector.select(2000);
if (count > 0) {
//遍历得到selectionkey 集合
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
//取出selectionkey
SelectionKey key = iterator.next();
//监听到了accpet
if (key.isAcceptable()) {
SocketChannel sc = listenerChannel.accept();
//将sc 注册到selector
sc.register(selector, SelectionKey.OP_READ);
//提示信息
System.out.println(sc.getRemoteAddress() + "上线了……");
}
if (key.isReadable()) {
//通道是可以读取的转态
readData(key);
}
//手动从集合中移动当前的selectionkey,防止重复操作
iterator.remove();
}
} else {
System.out.println("等待连接中……");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
//读取客户端消息
public void readData(SelectionKey key) {
//定义一个SocketChannel
SocketChannel channel = null;
try {
//得到channel
channel = (SocketChannel) key.channel();
//创建一个buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = channel.read(buffer);
//根据count的值进行读取
if (count > 0) {
//把缓冲区的数据变成字符串
String msg = new String(buffer.array());
//输出数据
System.out.println("from 客户端:" + msg);
//向其他的客户端转发消息
sendInfoToOtherClients(msg,channel);
}
} catch (IOException e) {
try {
System.out.println(channel.getRemoteAddress() + "离线了……");
//取消注册 关闭通道
key.cancel();
//关闭通道
channel.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
//转发消息给其他用户得通道
private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
System.out.println("服务器的转发消息中……");
//遍历 所有的注册到selector上的socketChannel 并排除自己
for (SelectionKey key : selector.keys()) {
//通过key 取出SocketChannel
Channel targetChannel = key.channel();
//排除自己
if (targetChannel instanceof SocketChannel && targetChannel != self) {
//转型
SocketChannel des = (SocketChannel) targetChannel;
//将msg 存储到buffer
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
//将buffer的数据写入到通道
des.write(buffer);
}
}
}
public static void main(String[] args) {
}
}
客户端
package com.zhuangxiaoyan.nio.groupchat;
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.Scanner;
import java.util.Set;
/**
* @Classname GroupChatClient
* @Description TODO
* @Date 2021/11/6 9:49
* @Created by xjl
*/
public class GroupChatClient {
//定义相关的属性
private final String Host = "127.0.0.1";
private final int PORT = 6667;
private Selector selector;
private SocketChannel socketChannel;
private String username;
//构造器 完成初始化工作
public GroupChatClient() throws IOException {
selector = Selector.open();
//连接服务器
socketChannel=SocketChannel.open(new InetSocketAddress(Host, PORT));
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//将channel 注册到selector
socketChannel.register(selector, SelectionKey.OP_READ);
//获得userbname
username = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(username + "is ok……");
}
//向服务器发送消息
public void sendInfo(String info) {
info=username+"说:"+info;
try {
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
//读取服务器回复的数据
public void readInfo(){
try {
int readChannels = selector.select();
if (readChannels>0){
//有可用的通道
Set keys = selector.keys();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if (key.isReadable()){
//获取到相关的读取通道
SocketChannel sc=(SocketChannel)key.channel();
ByteBuffer buffer=ByteBuffer.allocate(1024);
//读取
sc.read(buffer);
//把读取到缓冲区的数据成字符串
String msg=new String(buffer.array());
System.out.println(msg.trim());
}
}
//删除当前selectionkey 删除 防止重复操作
iterator.remove();
}else {
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//启动客户端
GroupChatClient chatClient = new GroupChatClient();
//启动一个线程
new Thread(){
@Override
public void run(){
while (true){
chatClient.readInfo();
try {
sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}.start();
//发送数据给服务器
Scanner scanner=new Scanner(System.in);
while (scanner.hasNextLine()){
String s = scanner.nextLine();
chatClient.sendInfo(s);
}
}
}