NIO群聊系统
这里面的知识比较全面,用到了我们之前学习的三大组件,首先我先来给大家介绍本系统的功能
服务端功能
最基本的当然是注册功能,也就是将serverSocketChannel注册进Selector,Selector负责调度事件
监听、读取客户端发来的消息
收到客户端的信息后将其转发到其它客户端,完成群聊功能
客户端功能
连接服务端,注册进Selector
读写消息
以上就是群聊系统最基本的功能,我们直接上代码,每句话的含义我都标在它的顶上了
服务端源码
package com.hecl.designmodels.NIO.GroupChat;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class GroupChatServer {
//定义一个选择器对象
private Selector selector;
//定义一个ServerSocketChannel对象
private ServerSocketChannel serverSocketChannel;
//养成习惯,对于这种不变的属性我们使用static final 来定义,且单词为全大写
private static final int PORT = 7000;
public GroupChatServer() {
try{
//获取选择器
selector = Selector.open();
//获取ServerSocketChannel
serverSocketChannel = ServerSocketChannel.open();
//绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
//将serverSocketChannel注册进selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch (Exception e){
e.printStackTrace();
}
}
//监听方法
public void listen(){
try{
while(true){
int count = selector.select();
//如果count大于0就说明有事件发生
if(count > 0){
//selector.selectedKeys()获取一个Set<SelectionKey>集合
//遍历SelectionKey
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
//获取目前遍历到的SelectionKey对象
SelectionKey key = iterator.next();
//有新连接创建
if(key.isAcceptable()){
//获取连接到服务器的SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
//将该通道设置为非阻塞
socketChannel.configureBlocking(false);
//注册进selector
socketChannel.register(selector,SelectionKey.OP_READ);
System.out.println(socketChannel.getRemoteAddress() + ": 已登录");
}
//发生有数据发送过来触发读事件
if(key.isReadable()){
//调用读取数据的方法
readMsg(key);
}
iterator.remove();
}
}
}
}catch (IOException e){
e.printStackTrace();
}
}
//读取数据方法
public void readMsg(SelectionKey selectionKey){
SocketChannel socketChannel = null;
try {
//通过key获取与之关联的SocketChannel
socketChannel = (SocketChannel) selectionKey.channel();
//创建缓冲区去接收通道内的数据
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
//得到通道内的数据
socketChannel.read(byteBuffer);
//在控制台打印消息
System.out.println("from " + socketChannel.getRemoteAddress() + "客户端 :" + new String(byteBuffer.array()));
//转发消息
sendMsgToOtherClients(new String(byteBuffer.array()),socketChannel);
}catch (IOException e){
try{
System.out.println(socketChannel.getRemoteAddress() + "下线");
//消毁selectionKey
selectionKey.cancel();
//销毁socketChannel
socketChannel.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
//向其他客户端转发消息,不包括自己
public void sendMsgToOtherClients(String msg,SocketChannel socketChannel){
try {
//获取所有的SelectionKey
for(SelectionKey OtherKey : selector.keys()){
//获取对应的socketChannel
Channel OtherChannel = OtherKey.channel();
//排除自身
if(OtherChannel instanceof SocketChannel && socketChannel != OtherChannel){
//向下转型
SocketChannel OtherSocketChannel = (SocketChannel) OtherChannel;
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
//将缓冲区的数据写入通道
OtherSocketChannel.write(byteBuffer);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
//启动服务端
GroupChatServer groupChatServer = new GroupChatServer();
//调用监听方法
groupChatServer.listen();
}
}
客户端源码
package com.hecl.designmodels.NIO.GroupChat;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import designModels.design_mode_07_BridgePattern.Pay;
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;
public class GroupChatClient {
//定义需要连接服务器的地址与端口号
private static final String HOST = "127.0.0.1";
private static final int PORT = 7000;
//定义选择器对象
private Selector selector;
//定义SocketChannel对象
private SocketChannel socketChannel;
//登录名
private String username;
public GroupChatClient(String username){
try {
//获取一个SocketChannel对象,并连接服务器
socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT));
//得到选择器
selector = Selector.open();
//设置非阻塞
socketChannel.configureBlocking(false);
//将通道注册进selector
socketChannel.register(selector, SelectionKey.OP_READ);
//得到登录名称
this.username = username;
String s = username + " 加入群聊";
System.out.println(s);
socketChannel.write(ByteBuffer.wrap(s.getBytes()));
}catch (IOException e){
e.printStackTrace();
}
}
public void sendMsg(String msg){
//数据拼接形成最终发送的消息
String info = username + " say : " + msg;
try{
//将缓冲区内的数据写入通道
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
System.out.println("I say : " + msg);
}catch (IOException e){
e.printStackTrace();
}
}
//参照服务端读取数据代码
public void readMsg(){
try{
int count = selector.select();
if(count>0){
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeySet.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
socketChannel.read(byteBuffer);
String msg = new String(byteBuffer.array());
System.out.println(msg);
}
//必须要移除,要不然收不到其它客户端的消息
iterator.remove();
}
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Scanner scannerName = new Scanner(System.in);
//启动服务
GroupChatClient groupChatClient = new GroupChatClient(scannerName.nextLine());
//新建一个线程循环调用读取数据的方法
new Thread() {
public void run(){
while(true) {
groupChatClient.readMsg();
try {
//休息2s
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}.start();
//获取输入消息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
//获取输入消息并调用sendMsg(String msg)
groupChatClient.sendMsg(scanner.nextLine());
}
}
}
我们来看看最终实现效果
服务器端
客户端1
客户端2
客户端3
前面发送的消息,后加入的客户端获取不到,这就是通过NIO实现的简单群聊啦
基础都打得差不多了,下一篇我们就应该正式进入Netty的章节啦,赞赞赞赞赞
完成:2021/3/28 20:20 ALiangX
转载请标注原作者,谢谢你们的支持,能给个小心心吗?