文件传输系统
概述
cs-auther 的文件传输系统基于 TCP 独立连接 实现,与主消息通道分离,支持大文件的分片传输、断点续传、进度监控和安全验证。
架构设计
┌─────────────────────────────────────────────────────────────────────────────┐
│ 文件传输架构图 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 客户端 │ │ 服务端 │ │
│ │ ┌───────────────┐ │ TCP:801 │ ┌───────────────┐ │ │
│ │ │ cFileClient │◄─┼───────────────────►┼─►│ cFileServer │ │ │
│ │ │ (发送/接收) │ │ 独立文件传输通道 │ │ (接收/发送) │ │ │
│ │ └───────┬───────┘ │ │ └───────┬───────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌───────▼───────┐ │ │ ┌───────▼───────┐ │ │
│ │ │ 业务事件 │ │ │ │ bFileManage │ │ │
│ │ │ OnSending │ │ │ │ (文件管理) │ │ │
│ │ │ OnRecving │ │ │ │ 自动归档存储 │ │ │
│ │ │ OnSendFinish │ │ │ │ 日志记录 │ │ │
│ │ │ OnRecvFinish │ │ │ └───────────────┘ │ │
│ │ └───────────────┘ │ │ │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 主消息通道 (TCP:800) │ │
│ │ 用于控制命令和文件列表查询 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘端口分配
| 服务 | 端口 | 说明 |
|---|---|---|
| 消息服务 | 800 | JSON 协议,处理业务逻辑 |
| 文件服务 | 801 | 分片传输,处理文件数据 |
核心组件
1. cFileClient (客户端)
位于 share/cFileClient.cls,封装了客户端文件传输的所有功能。
主要属性
| 属性 | 类型 | 说明 |
|---|---|---|
ChunkSize | Long | 分片大小,默认 32KB |
LastError | String | 最后一次错误信息 |
主要方法
vb
' 连接到文件服务器
Public Sub StartConnectTo(ByVal Server As String, Optional ByVal Port As Long = 803)
' 断开连接
Public Sub Disconnect()
' 发送文件到服务端
Public Function SendToServer(ByVal FileName As String) As Boolean
' 服务端回调:接收文件分片
Public Sub RecvFromServer(Inst As cWinsock, Data As cJson)
' 服务端回调:请求下一个分片
Public Sub SendMeNextFileChunk(Inst As cWinsock, Data As cJson)
' 服务端回调:发送完成确认
Public Sub OnSendComplete(Inst As cWinsock, Data As cJson)事件
vb
' 正在发送文件
Event OnSending(ByVal FileName As String, ByVal CurrentChunk As Long, ByVal TotalChunks As Long, ByVal Percent As Long)
' 正在接收文件
Event OnRecving(ByVal FileName As String, ByVal CurrentChunk As Long, ByVal TotalChunks As Long, ByVal Percent As Long)
' 发送完成
Event OnSendFinish(ByVal FileName As String)
' 接收完成
Event OnRecvFinish(ByVal FileName As String, ByVal FilePath As String)2. cFileServer (服务端)
位于 share/cFileServer.cls,处理多客户端并发文件传输。
主要属性
| 属性 | 类型 | 说明 |
|---|---|---|
ChunkSize | Long | 分片大小,默认 32KB |
LastError | String | 最后一次错误信息 |
主要方法
vb
' 启动文件服务
Public Sub StartMe(Optional Port As Long = 801, Optional IP As String = "0.0.0.0")
' 停止服务
Public Sub StopServer()
' 发送文件给指定用户
Public Function SendToClient(ByVal User As String, ByVal FileName As String) As Boolean
' 客户端回调:绑定Token
Public Sub BindClientToken(Inst As cWinsock, Data As cJson)
' 客户端回调:接收文件分片
Public Sub RecvFromClient(Inst As cWinsock, Data As cJson)
' 客户端回调:请求下一个分片
Public Sub SendMeNextFileChunk(Inst As cWinsock, Data As cJson)事件
vb
' 客户端正在上传
Event OnClientSending(ByVal User As String, ByVal FileName As String, ByVal CurrentChunk As Long, ByVal TotalChunks As Long, ByVal Percent As Long)
' 客户端上传完成
Event OnClientSendFinish(ByVal User As String, ByVal FileName As String, ByVal FilePath As String)
' 正在发送给客户端
Event OnClientRecving(ByVal User As String, ByVal FileName As String, ByVal CurrentChunk As Long, ByVal TotalChunks As Long, ByVal Percent As Long)
' 发送给客户端完成
Event OnClientRecvFinish(ByVal User As String, ByVal FileName As String)3. bFileManage (业务管理层)
位于 src-server/bFileManage.cls,在 cFileServer 之上封装业务逻辑。
主要功能
- 自动归档:接收的文件自动按用户/年月分类存储
- 独立日志:文件传输日志与系统日志分离
- 文件管理:提供文件列表查询、删除等接口
存储结构
Server.exe 同目录
└── files/
└── {用户名}/
└── {年}/
└── {月}/
└── 文件名_年月日_时分秒.ext路由接口
vb
' 获取当前用户的文件列表
Public Sub GetMyFilesList(Inst As cWinsock, Data As cJson)
→ 响应: FileManage/OnFilesList
' 删除指定文件
Public Sub DeleteMyFile(Inst As cWinsock, Data As cJson)
→ 响应: FileManage/OnFileDeleted传输协议
分片数据结构
文件传输使用 JSON 协议,每个分片包含以下字段:
json
{
"FileID": "GUID字符串",
"FileName": "原始文件名",
"FileSize": 1048576,
"ChunkSize": 32768,
"TotalChunks": 32,
"CurrentChunk": 5,
"NextChunk": 6,
"ChunkData": "Base64编码的数据...",
"IsLastChunk": false
}传输流程
客户端 → 服务端(上传)
┌─────────┐ ┌─────────┐
│ Client │ │ Server │
└────┬────┘ └────┬────┘
│ │
│ 1. 发送第1片 (CurrentChunk=1) │
│ ────────────────────────────────────────────────>│
│ action: "FileServer/RecvFromClient" │
│ │
│ │──┐ 写入临时文件
│ │ │ files/tmp/{FileID}.tmp
│ │<-┘
│ │
│ 2. 请求第2片 │
│ <────────────────────────────────────────────────│
│ action: "FileClient/SendMeNextFileChunk" │
│ { "NextChunk": 2 } │
│ │
│ 3. 发送第2片 │
│ ────────────────────────────────────────────────>│
│ ... │
│ │
│ n. 发送最后一片 (IsLastChunk=true) │
│ ────────────────────────────────────────────────>│
│ │
│ n+1. 发送完成确认 │
│ <────────────────────────────────────────────────│
│ action: "FileClient/OnSendComplete" │
│ │──┐ 归档到目标目录
│ │ │ files/{user}/{yyyy}/{mm}/
│ │<-┘服务端 → 客户端(下发)
┌─────────┐ ┌─────────┐
│ Client │ │ Server │
└────┬────┘ └────┬────┘
│ │
│ 1. 发送第1片 │
│ <────────────────────────────────────────────────│
│ action: "FileClient/RecvFromServer" │
│ │
│──┐ 写入临时文件 │
│ │ {FileID}.tmp │
│<-┘ │
│ │
│ 2. 请求下一片 │
│ ────────────────────────────────────────────────>│
│ action: "FileServer/SendMeNextFileChunk" │
│ │
│ 3. 发送下一片 │
│ <────────────────────────────────────────────────│
│ ... │
│ │
│ n. 发送完成 │
│ <────────────────────────────────────────────────│
│ (服务端清理资源) │身份验证
文件传输通道使用 Token 绑定机制进行身份验证:
连接建立流程
vb
' 客户端连接成功后,自动发送 Token 绑定
Private Sub mTcp_Connect(Client As VBMANLIB.cWinsock)
With New cJson
.Item("token") = Common.UserToken ' 从登录获取的凭证
Set .Item("user") = Insts.CurrentUser.Root ' 当前用户信息
Common.SendTo Client, "FileServer/BindClientToken", .Root
End With
End Sub服务端绑定处理
vb
Public Sub BindClientToken(Inst As cWinsock, Data As cJson)
Inst.CurrentUserToken = Data("token")
Inst.CurrentUserInfo.Decode Data("user")
Inst.CurrentUser = Data("user")("UserName")
End Sub注意:文件传输通道继承主消息通道的 Token,连接时需要显式绑定才能通过中间件验证。
使用示例
客户端发送文件
vb
Dim WithEvents FileClient As cFileClient
' 初始化并连接
Set FileClient = New cFileClient
FileClient.StartConnectTo "192.168.1.100", 801
' 发送文件
If FileClient.SendToServer("C:\Documents\report.pdf") Then
Debug.Print "开始发送..."
Else
Debug.Print "发送失败: " & FileClient.LastError
End If
' 监听进度
Private Sub FileClient_OnSending(ByVal FileName As String, ByVal CurrentChunk As Long, ByVal TotalChunks As Long, ByVal Percent As Long)
ProgressBar1.Value = Percent
Label1.Caption = "发送中: " & Percent & "%"
End Sub
' 发送完成
Private Sub FileClient_OnSendFinish(ByVal FileName As String)
MsgBox "文件发送完成: " & FileName
End Sub服务端主动下发文件
vb
' 在 bFileManage 中调用
Public Sub SendFileToUser_Click()
Dim UserName As String
UserName = "zhangsan"
If bFileManage.SendFileToUser(UserName, "C:\Files\notice.pdf") Then
Debug.Print "开始发送给 " & UserName
End If
End Sub获取文件列表
vb
' 客户端请求
With New cJson
Common.SendTo Insts.TcpClient, "FileManage/GetMyFilesList", .Root
End With
' 客户端接收响应 (在 bFileManage 中实现)
Public Sub OnFilesList(Data As cJson)
' Data.Root 包含文件列表数组
For Each FileItem In Data.Root
ListView.AddItem FileItem("Name")
Next
End Sub配置说明
分片大小调整
vb
' 客户端
FileClient.ChunkSize = 64& * 1024& ' 设置为 64KB
' 服务端
bFileManage.StartMe Port
mFileServer.ChunkSize = 1024& * 1024& ' 设置为 1MB建议:
- 局域网环境:64KB ~ 1MB
- 互联网环境:16KB ~ 32KB
- 超大文件:增大分片可减少协议开销
错误处理
常见错误
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
| 当前有正在发送的文件 | 同时发起多个发送请求 | 等待当前传输完成或创建新的 cFileClient 实例 |
| 用户不在线 | 服务端下发时用户已断开 | 检查用户在线状态后再发送 |
| 临时文件不存在 | 文件被清理或权限问题 | 检查目录权限和磁盘空间 |
事件中的错误处理
vb
Private Sub FileClient_OnSendFinish(ByVal FileName As String)
If FileClient.LastError <> "" Then
MsgBox "传输出错: " & FileClient.LastError
Else
MsgBox "传输成功!"
End If
End Sub性能优化
- 并发传输:每个用户独立维护传输状态,支持多用户同时传输
- 分片缓存:接收时直接追加写入临时文件,减少内存占用
- Base64 编码:兼容 JSON 传输,相比二进制略增 33% 体积
- 独立通道:文件传输与业务消息分离,避免阻塞
安全建议
- 文件类型过滤:在
bFileManage.OnClientSendFinish中检查扩展名 - 大小限制:在发送前检查
FileSize是否超过限制 - 路径校验:
DeleteMyFile中已包含安全校验,确保只能删除自己的文件 - Token 过期:与主通道 Token 生命周期保持一致