type
status
date
slug
summary
tags
category
icon
password
计算机网络实验报告------基于Socket 的TCP 文件传输通信

一、实验任务:

1. 实现两个程序:服务器端(server)和客户端(client)
2. 客户端发送文件传输请求,服务器端将文件数据发送给客户端
o 两个程序均在命令行方式下运行
o 客户端在命令行指定服务器的IP 地址和文件名
o 为防止重名,客户端将收到的文件改名后保存在当前目录下
o 要求至少能传输一个文本文件和一个图片文件
o 在服务器端,应输出:
1. 客户端的IP 地址和端口号
2. 发送的文件数据总字节数
3. 必要的差错报告
o 在客户端,应输出:
1. 新文件名,输出总字节数
2. (如果发生差错的话)差错报告

二、实验内容:

用C++编写客户端和服务端,二者可以进行基于TCP 的文件传输通信。二者均
在命令行的方式下运行。客户端可以在命令行指定服务器的IP 地址和端口号,二
者即可建立连接。建立连接后,客户端和服务端之间可以进行通信,利用这一特点
不但可以进行文本和图片等文件的传输,也可以让服务器和客户端进行交互。
本工具采用类似于Linux SSH 命令行的形式,由客户端接受用户的输入,处理后,通过TCP 传输给服务器,服务器对客户端传输来的命令进行识别处理,并给出对应的回应。为了让服务器和客户端能够区分文件和命令(或回复消息),采用发送不同的包进行预告,然后进行数据传输,最后传输不同的结束标识包。所以每一
次数据传输,不管是命令还是文件数据,都是至少三个包:开始预告包,数据包,结束标识包。其中标识包都是2 字节大小。这样做利用了TCP 不会丢包且不会乱序的特性。

三、实验环境:

一台/多台Windows 系统的电脑。这些电脑可以处于同一局域网,或者作为服务器的电脑具有可访问的公网IP,多个客户端可以同时与服务器建立连接。

四、软件设计:

1. 程序流程:
使用C++创建了这样几个类:Server 类、ClientHandler 类、Client 类、ClientProcessor 类。在宏定义中可以选择是作为Server 编译还是Client 编译。当作为Server 启动时,将会创建Server 类和ClientHandler(客户处理)类的实例;当作为客户端启动时,则会创建Client 类和ClientProcessor(客户工作)类的实例用来与服务端建立连接。下面将分别展述服务端和客户端的具体细节。
下面为基础命令以及数据开头结尾格式的宏定义和常量
#define LS_COMMAND "1s"
#define DOWNLOAD_COMMAND "get"
#define UPLOAD_COMMAND "upload"
#define DELETE_COMMAND "rm"
#define CD_COMMAND "cd"
#define MKDIR_COMMAND "mkdir"
constexpr char start_cm_response_send[3] = "\x01\xfe"; constexpr char start_bytearr_response_send[3] = "\x04\xfe"; constexpr char end_command_send[3] = "\x02\xff";
constexpr char end_bytearr_response_send[3] = "\x05\xff";
#define upload_timeout 5
2. 服务端:
启动后,首先初始化一个Server 的实例,在这个实例中将初始化Socket 相关
资源,并循环检测是否有客户端请求与自己建立连接。如果有客户与服务端建立连
接,那么将为每个客户初始化一个ClientHandler 实例,用来处理服务器与这一客户的事务,其中完成连接建立后,每个ClientHandler 都将启动一个自己的线
程处理用户传来的数据,以此来支持多客户的连接。
连接建立后,为每个客户所分配的ClientHandler 实例将保存和处理该用户的所有请求。其中,权限为private 的变量有,SOCKET 类型的clnt_sock 用来保存和客户之间的socket 连接;fs::path 类型的变量currentPath 用来保存当前客户的工作目录。当ClientHandler 的构造函数被执行的时候,将会给二者写入值,其中currentPath 初始值会设置为当前服务端软件所在目录的路径,如果用户使用了cd 相关的命令进入新的文件夹,则会发生改变。
首次建立连接,服务端将返回当前工作目录的路径。随后,将在为每个客户分配
的handle 线程中,单独处理每个客户的请求。
服务端收到命令,将执行对应操作。所有命令格式如下,如果是不符合格式的命
令,或者命令参数有错误,分别会返回不同的错误码给客户端。
软件中所设计的命令如下:
 Ls 命令:返回当前目录下所有文件/文件夹名称,并表明类型。如果是空目 录将返回Empty Dictionary。
 Get 命令:发送文件给客户(客户下载)。服务端的异常处理:不存在的
文件或者请求下载了文件夹将返回不同的固定错误码,客户端可识别并提示
用户,同时不再请求文件传输。客户端的异常处理将在下一部分提出。
 Rm 命令:从服务器上删除文件。后跟文件名,可以删除文件。如果输入 rm -r <目录名>,则可以删除指定目录。如果不加-r 而请求删除文件 夹,或者-r 删除文件,或者请求删除不存在的文件/目录,则分别会返回
不同的错误码给客户端。正常删除则返回成功删除的提示码给客户。
 Upload 命令:上传文件到服务器,后跟文件名。在上传开始之前服务器会 检测是否已经有同名文件,如果命令是upload <文件名>则提示文件已存 在,不接收文件。如果命令为upload -o <文件名>,o 表示overwrite 则
正常接收文件并且会覆盖保存。收到消息后,服务器会返回消息提示已经准备接收文件,并根据宏定义设置超时。数据将分段被发送,每一段都会insert 到一个vector 中。全部接收完成后会收到来自客户端的提示码,将根据文件保存情况分别回复成功和失败。
 Cd 命令:进入目录。使用cd / <目录> 可以根据绝对路径进入目录,使用 cd<目录名>则会进入当前文件夹下的目录,使用cd 不跟参数可以回到服 务器软件所在目录,使用cd ..可以返回上一级目录。异常处理包括目录不 存在以及是文件不是目录的情况,分别返回错误码给客户端。
 Mkdir 命令:新建文件夹。使用Mkdir <目录名> 可以在当前工作目录下 新建一个文件夹,将根据能否成功创建返回不同的提示码给客户端。
其他所有未考虑到的错误,则会交给SEH 进行处理。
提示码表:
P// 当前工作目录(后加绝对路径)
FAIL_INVALID_COMMAND 不存在的命令
FAIL_INVALID_COMMAND_FORMAT 错误不合法的指令格式/参数SUCCESS_START_DOWNLOADING 可以开始下载
SUCCESS_RDY 传输文件状态就绪
SUCCESS_SEND_FINISH 传输文件发送结束
SUCCESS_CREATED 成功创建目录
SUCCESS_FINISH_RECV 成功接收完成
FAIL_NOT_EXIST 错误文件不存在
SUCCESS_DELETED_DIR 成功删除目录
FAIL_UNABLE_DELETE_DIR 无法删除目录
FAIL_NOT_A_DIR 错误要删除的是文件而不是目录
FAIL_EXISTED_NO_OVERWRITE 错误文件已存在并且没有覆盖参数FAIL_RECV_TIMEOUT 错误文件接收超时
FAIL_WRITING_FILE 错误文件无法写入到磁盘
FAIL_NO_DIR 无法cd,目录不存在/是文件
FAIL_UNABLE_CREATE_DIR 错误无法新建文件夹
3. 客户端:
客户端启动后,会自动检测当前目录下的config.ini 文件并尝试读取已保存的服务器IP 地址和端口号,尝试建立连接。如果读取失败,或者建立连接失败,则会让用户重新输入。如果用户在启动的时候同时按住ctrl,也会允许用户重新输入配置。连接成功后会覆盖保存到config.ini 中
当作为Client 启动时,将初始化一个Client 类实例,构造函数将保存服务端的ip 和端口号,作为两个private 变量。该实例则拥有一个ClientProcessor 类,通过createProcessor 函数启动Processor 的线程。Client 类自己有一个线程,用来处理用户输入和信息发送,另一个ClientProcessor 类的线程以阻塞方式等待TCP 消息的接收。
InputHandler:
作为输入的处理,只会检测用户的exit/upload/get 指令。
如果用户输入exit,则主动关闭套接字并退出。
如果用户输入upload,则会检测当前目录下有没有要上传的文件,如果没有则报
错,不发送任何指令给服务器。如果有的话,则发送指令给服务器。服务器根据服
务器目录是否有同名文件,再进行不同的操作。如果接收到错误码,则停止。
如果用户输入get,则会检测当前目录下是否已经存在文件。如果已经存在,给出提示,报错。如果用户输入的是get -o,则会把新下载的文件文件名加上“.L”
后,保存下来,避免覆盖。
下载开始时,会收到来自服务器的信息,提示文件传输开始并告知文件总大小,
传输一旦开始,将根据已经收到的字节数和总大小动态显示出进度条。
上传开始时,会将文件指针移动到末尾,从而获取整个文件的大小,传输一旦开
始,将会根据已上传字节数和总大小动态显示出进度条。
4. 软件原理图和软件流程图:
软件原理图:
notion image
软件流程图:
notion image
5. 通信协议:
notion image
每一条指令/文件分片,都将发送三个包,头和尾分别是宏定义的固定数值。因为TCP 传输特性,不会丢包,不会乱序,所以一定能保证收发的正常进行。其中对于文件传输开始/就绪/结束的提示码,也是作为指令内容以指令格式传输。

五、实验结果演示及分析:

1. 使用宏定义控制server 和client 的编译。
2. 服务器所处目录:
notion image
3. 运行服务器和客户端,客户端自动读取config.ini 中的配置并与服务器建 立连接。客户端的命令行提示目前服务器上所处目录。
notion image
4. 使用ls 命令列出当前服务器目录的所有文件。
notion image
5. 进行传输文本和图片的测试。
首先是文本传输。下载服务器目录的“测试文本下载内容abc.txt”,这个txt 的内容就是abc
notion image
从预览可见,下载文本的内容正确。
再下载图片内容。图片名称是测试传输图片.png
notion image
通过预览可见图片正确无误。
6. 文件大小显示。图片是68771 字节,合0.07MB。文本文件内容只有abc 三个字母,所以大小为3 字节。大小太小,换算成MB 四舍五入为0。
notion image
notion image
7. 在客户端输入mkdir 新建一个目录,ls 发现成功。
notion image
8.使用cd 命令进入目录
notion image
9.使用cd ..回到上一级目录。随着cd 命令命令提示符也会改变。
notion image
10.使用rm -r 命令删除目录
notion image
使用rm 命令删除test.txt,ls 发现成功
notion image
11.新建一个testrm 的文件夹,尝试用rm 删除,发现rm 不能删除文件夹,rm
  • r 则可以删除。
notion image
12.尝试删除不存在的文件,服务器报错返回并打印
notion image
13.尝试用rm -r 命令删除文件,服务器报错。但是rm 命令可以删除。避免误
操作。
notion image
14.使用upload 命令上传沙丘.mp4,一个520mb 的电影,并观察进度。ls 发现
上传成功
notion image
notion image
notion image
15.再次upload 沙丘.mp4,发现同名文件会导致报错
notion image
16.使用upload -o 覆盖上传,成功
notion image
notion image
17.Get 命令下载,get 沙丘.mp4,同名文件已经存在,报错。
notion image
18.Get -o 命令下载,存放到后缀.L 的文件中
notion image
notion image
notion image
19.尝试下载不存在的文件,服务器报错返回并打印
notion image
20.同时启动两个客户端与服务器建立连接
notion image
21.分别cd 进入不同目录
notion image
22.分别ls,各自看到的是各自工作目录的内容,互不干扰。
notion image

六、实验总结和心得体会

1. 遇到的问题和解决过程:
服务器无法同时处理多个客户端的请求,等待TCP 包的时候会阻塞:为每个客户端新建一
个类的实例,并分别使用线程进行客户端的请求管理。
文件操作往往会涉及到同名文件/已存在文件等问题,因此添加了多个命令,让客户端/服务器根据命令后的参数作出不同的操作。
字符串处理:用户的命令往往不会输入的非常精确,可能遇到命令与参数之间出
现了多个空格的情况,则需要程序具有鲁棒性,能对用户不规范格式的正确命令作
出处理。
进度显示:采用\r 返回行首以覆盖之前的输出,std::cout.flush()确保输出立即
显示,以此达到让进度条每次在同一行更新,并显示出来新的进度。
2. 收获和提高
通过实践深入理解了传输层的TCP 协议和应用层的Socket 编程接口,以及它们在网络OSI 模型中的位置和作用
掌握了使用C/C++的API(比如socket(), bind(), listen(), accept(), connect(), send(), recv()等)来创建和管理TCP 连接
了解、掌握了线程的概念和对线程的使用,与《操作系统》的知识相结合,利用
线程并行同时处理多个客户端的事务。
掌握了对套接字编程中可能遇到的各种异常和错误情况进行处理,例如网络延
迟、断开连接等。
学会了设计一套适用于自己对应用开发要求的应用层协议,比如定义数据包的格
式、错误控制、同步机制等。
掌握如何将文件数据封装到协议数据包中,以及如何在接收端正确解码并重建文
学习了如何将软件分解为多个模块/对象,将一个功能模块封装成一个类,提升代码的可读性和可维护性,移植性。提高了使用调试工具的能力,编写并运行单元测
试和集成测试来确保程序的稳定性。
3. 本实验题目的设计和安排
完成实验所需要的时间相对较长。

七、附录

Main.cpp 根据宏定义进行不同版本的编译
Includes.h 包含文件
Server.h 服务器、客户端、处理器、工作器实现
 
操作系统实验——OpenHarmony编译iOS下的Unity逆向:地铁跑酷
Loading...
Lynnette177
Lynnette177
建议开着梯子访问站点。图片是直接从Notion获取的,不开梯子容易看不见图片。
Latest posts
iOS免越狱hook与patch框架和免越狱dylib注入打包
2025-2-19
iOS liapp bypass+Unity逆向+tweak开发
2025-2-7
手动实现的seh
2025-1-25
iOS逆向——某跑步软件的文件上传
2024-11-30
支付宝小程序逆向
2024-10-24
iOS下的Unity逆向:地铁跑酷
2024-10-1
Announcement
🎉2024.6.9 上线🎉