网络编程v4--群聊和私聊的实现

一、前言

前面的版本实现了聊天信息广播,但是没有对聊天方式进行区分。v4版本进行了改进,将聊天方式细分为私聊和群聊,私聊情况下仅会话双方可见信息(这里以@用户 信息的信息作为私聊公式),所以以@作为区分聊天方式的主要特征,带有@的信息要进行解析并私发。

二、 代码改动和解释

主要变化就是两个函数:

1.Client客户端类中的发信息函数send

2.Server服务端类中的收信息函数receive

三、发消息函数send具体改动和解释

**1.**首先明确私聊消息协议(@+用户名+空格+信息),所以先对信息首个字符进行判断,如果是"@"那么就是私聊信息,反之走群聊模式

2.私聊模式下,先发送特征数字1,接着,由于需要规定用户名长度和消息长度,先获取用户名字符串部分和消息字符串部分

这里通过" "索引,再通过subString获取两个字符串

3.得到两个字符串之后,获取其长度并规定消息长度协议,分别发送

4.群聊模式下,先发送特征数字2,接着读取消息内容即可

java 复制代码
public void send(String msg){
        // 消息类型 好友名称长度 好友名称 信息类型 消息长度 消息内容
        // 先通过@ 来区分是否是私聊
        // 示例消息:@小王 你好
        char c=msg.charAt(0);
        try {
            OutputStream os=socket.getOutputStream();
            if(c=='@'){
                //私聊
                int index=msg.indexOf(" ");//获取空格索引位置
                String FriengName=msg.substring(1,index);//从@后开始截取,到空格前
                String massege=msg.substring(index+1);//从空格后一行到结束
                System.out.println(FriengName+" "+massege);
                os.write(1);//私聊标志
                os.write(FriengName.getBytes().length);//发好友名称字节数
                os.write(FriengName.getBytes());//将好友名称转成字节数组再发送
                os.write(massege.getBytes().length);//发送消息字节数
                os.write(massege.getBytes());//发消息
            }else {
                //群聊
                os.write(2);//群聊标志
                os.write(msg.getBytes().length);
                os.write(msg.getBytes());
            }

            os.flush();//刷新
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

四、Server服务类receive函数改动与解释

1.由于前面客户端send函数先通过第一个数字区分私聊和群聊,所以先读取第一个数字,利用分支结构分类讨论

2.如果特征数字是1,则走私聊信息处理流程

1先获取到好友名称的长度和好友名称

2再获取信息长度和信息名称

3return语句用"#"分割各个部分,便于后续用split函数处理

3.如果特征数字为2,则群聊处理,仅需要获取信息内容

java 复制代码
public String receive(InputStream inputStream){
        try {
            //读取消息类型 私聊1 群聊2
            int type=inputStream.read();
            if(type==1){
                //私聊,先协议好友名长度
                int FidName_length=inputStream.read();
                byte[] FidNameData=new byte[FidName_length];
                inputStream.read(FidNameData);
                //规定信息长度
                int Msg_length=inputStream.read();
                byte[] MsgData=new byte[Msg_length];
                inputStream.read(MsgData);
                System.out.println("收到一条发送给"+new String(FidNameData,StandardCharsets.UTF_8)+"的消息:"+new String(MsgData,StandardCharsets.UTF_8));
                return "1"+"#"+new String(FidNameData,StandardCharsets.UTF_8)+"#"+new String(MsgData,StandardCharsets.UTF_8);
            }else {
                //群聊
                int length=inputStream.read();
                if(length==-1)return null;
                byte[] data=new byte[length];
                inputStream.read(data);
                //返回特定格式字符串
                return "2"+"#"+new String(data,StandardCharsets.UTF_8);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

五、handleClient函数改动

1.先通过split函数,将receive函数返回的语句进行拆分,私聊分成三个部分(特征数字+好友名称+信息内容),群聊部分则分成两个部分(特征数字+信息)

2.私聊情况下,进行判断,利用增强型for循环,当且仅当遍历到目标好友时才发送

3.群聊情况下,按照原本广播形式进行群发

java 复制代码
public void handeleClient(Socket client,String name){
        new Thread(() -> {
            try {
                InputStream inputStream=client.getInputStream();
                while (true){
                    String msg=receive(inputStream);//在此根据receive返回值进行区分私聊还是群聊
                    if(msg==null)break;
                    String [] msg_split=msg.split("#");
                    if(msg_split[0].equals("1")){
                        //私聊
                        String FriengName=msg_split[1];
                        String massege=msg_split[2];
                        for(ClientUser s:sockets){
                            if(s.account.equals(FriengName)){
                                send(s.socket,name+"发来的私聊消息:"+massege);
                            }
                        }
                    }else {
                        //群聊
                        for(ClientUser s:sockets){
                            if(s.socket!=client){
                                send(s.socket,name+"的消息:"+msg_split[1]);
                            }
                        }
                    }
                    System.out.println(client.getInetAddress()+":"+client.getPort()+"->"+msg);

                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }).start();
    }

六、效果展示

相关推荐
梦帮科技3 小时前
LoRA / QLoRA 低秩矩阵流形更新:SVD 分解、秩与缩放因子 α 的物理意义与梯度稳定性保障
网络·深度学习·神经网络·线性代数·矩阵·架构·数据挖掘
打工仔折腾 AI3 小时前
飞牛OS上用Docker Compose部署ExerciseDiary运动记录并配置远程访问
运维·人工智能·后端·python·docker·容器·ai agent 实战
天远数科4 小时前
零信任架构实战:基于天远运营商三要素简版V即时版查询构建自动化买手实名核验网关
运维·人工智能·架构·自动化
阿钱真强道4 小时前
28 嵌入式操作系统 | 网关加 TCP 命令通道:粘包/半包怎么切
网络·网络协议·tcp/ip·粘包
HY小宝F4 小时前
树莓派智能摄像头系统 30 天重学计划之 Day 03 · 网络基础:建立“包“的思维 —— 学习总结
网络·学习
ayaya_mana4 小时前
MCTier 组网联机工具:安装、配置与私有化部署
网络·windows·内网穿透·联机
天衍四九-4 小时前
Docker Compose企业实战系列(八·终章):多环境隔离实战,开发/测试/生产一键切换
运维·docker·容器
故七月4 小时前
GEO 信源评估体系:如何判断一条网页信源能否被大模型采信
前端·网络·人工智能
hasty4 小时前
一次 WebSocket 握手为何能终止进程?Undici CVE-2026-19534 的异常边界
网络·websocket·网络协议
C++ 老炮儿的技术栈5 小时前
有符号变量与无符号变量的区别
服务器·开发语言·前端·数据结构·c++·算法·c