网络编程v4pro--实现聊天室文件传输功能

一、前言

聊天室v4是一个基于Java Socket的多人聊天系统,包含服务端,命令行客户端,GUI客户端和用户信息类。原版本功能只支持私聊和群聊文本和表情两种信息,但不支持发送文件。本pro版新增文件信息的收发功能。

二、问题瓶颈分析

瓶颈1:长度字段只有一字节。所有消息长度通过os.write(data.length)发送,这只写了一个字节,最大只能表示255字节,文件动辄几KB到几MB,255字节根本不够用

瓶颈2:Server服务端向Client用户端发信息的时候没有类型区分,服务端send方法没有区分消息类型,客户端无法区分消息类型,会把文件二进制数据当文本解析导致乱码

瓶颈3:read函数不保证一次读完。原版本代码中in.read(data);调用一次就认为读完了。TCP是流式协议,一次read函数可能只返回部分数据,文件传输必须循环读取。

三、函数1(Server.send())改动和解释

改动原因:原来的客户端无法区分消息类型。改造后的服务端发给客户端的消息有两种,需要加一个类型前缀的字节来区分

解释

1.在方法开头加了 os.write(0),表示这条消息是文本类型

2.后续的 [长度][内容] 格式完全不变,原有文本收发逻辑不受影响

3.客户端 receive() 会先读这个类型字节,type=0 走文本逻辑,type=1 走文件逻辑

4.注意:这个类型前缀只用于 Server→Client 方向,Client→Server 方向仍然用原有的 type=1~4 体系

四、函数2(Server.sendFile())新增

新增原因

服务端收到客户端发来的文件后,需要把文件转发给目标客户端。原有的 send() 只能发文本(类型0),需要一个新方法按文件协议(类型1)发送文件。

解释

**1.类型码 1:**告诉客户端接下来是文件消息,走文件接收逻辑

**2.发送者名称:**让接收者知道文件是谁发的。长度用 1 字节(名字不会超过 255 字节)

**3.文件名:**让接收者知道文件叫什么,保存时用原名。长度也是 1 字节

4.文件长度用 4 字节 int: 这是关键改动。OutputStream.write(int) 每次只写低 8 位,一个 int 有 4 字节,需要通过位移拆成 4 次写入,最大可表示约 2GB

5.文件数据: 直接 os.write(fileData) 把整个字节数组写入输出流

五、函数3(Server.readFully())新增

解释:readFully(): 循环调用 in.read() 直到缓冲区填满。每次读取从 totalRead 位置开始,读 buffer.length - totalRead 个字节,累加 totalRead,直到读满或流关闭。

六、函数4(Server.handleClient())逻辑改动解释

七、函数5(Client.sendFile())新增

java 复制代码
public void sendFile(String filePath,String friendName){
        try {
            java.io.File file=new java.io.File(filePath);
            if(!file.exists()||!file.isFile()){
                System.out.println("文件不存在: "+filePath);
                return;
            }
            byte[] fileData=Files.readAllBytes(file.toPath());
            String fileName=file.getName();
            OutputStream os=socket.getOutputStream();

            if(friendName!=null&&!friendName.isEmpty()){
                // 私聊文件 type=5
                // 协议:[5][好友名长度][好友名][文件名长度][文件名][文件长度4字节][文件数据]
                os.write(5);
                os.write(friendName.getBytes().length);
                os.write(friendName.getBytes());
            }else{
                // 群聊文件 type=6
                // 协议:[6][文件名长度][文件名][文件长度4字节][文件数据]
                os.write(6);
            }
            os.write(fileName.getBytes().length);
            os.write(fileName.getBytes());
            int fileLen=fileData.length;
            os.write((fileLen >> 24) & 0xFF);
            os.write((fileLen >> 16) & 0xFF);
            os.write((fileLen >> 8) & 0xFF);
            os.write(fileLen & 0xFF);
            os.write(fileData);
            os.flush();
            System.out.println("文件 "+fileName+" 已发送 ("+fileLen+"字节)");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

解释

  • 读取本地文件: Files.readAllBytes() 一次性把文件读成 byte\[\],简单直接。注意大文件可能占内存,生产环境应分块发送
  • **type=5 vs type=6:**私聊多写一个好友名字段(1字节长度 + 内容),群聊直接跳过。后续的文件名 + 文件长度 + 文件数据两种类型完全一致
  • 文件长度写入:Server.sendFile() 一样,用位移拆 4 字节写入。两端写入方式必须对称

八、效果展示

相关推荐
小小、码农1 小时前
Linux进程控制:从fork到exec,手写一个简易Shell
linux·运维·服务器
程序员黑豆1 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
命运之光1 小时前
【C语言完整代码】就诊信息管理系统
java·c语言·开发语言
不会就选b1 小时前
Linux之进程间通信(二)---模拟进程池
linux·运维·服务器
marvelyu2 小时前
每天10分钟学会OceanBase系列(Day 20):跨机房容灾实战——构建多数据中心高可用架构
java·大数据·数据库
NeilYuen2 小时前
用UDP实现向DNS服务器请求IP地址
服务器·tcp/ip·udp
shengnan_wsn3 小时前
【协议】【TCP】
网络·网络协议·tcp/ip
烟雨江南7853 小时前
2026汽车制造与新能源整车柔性产线Agentic-Workflow白皮书:跨APS_MES_ERP多智能体动态排产与装配容错实战
大数据·网络·人工智能·自动化·ai客服·企业agent
AI人工智能+电脑小能手3 小时前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦