成功有内容回复ok#<数据>
成功无内容回复ok#
用户发来命令后,解析并通过exec的方式执行相关命令,返回相应结果。可能需要返回执行命令的结果,比如命令打印到屏幕的信息需要返回给用户,这时我们把子进程写入stdout的数据写入无名管道(需要用dup系列方法将管道的写端覆盖stdout),父进程从无名管道中拿到数据返回给用户
交互部分直接使用exec的方式实现,上传下载的操作需要封装函数完成
int dup(int oldfd)
int dup2(int oldfd, int newfd)
vscode调试的时候需要将launch.json的program修改为需要调试的ELF文件
目前execvp方法不支持通配符*
- 管道需要循环读取
- 支持cd等命令
设计文件传输协议:注意粘包问题(两个数据包一次recv的收到了,此时就会阻塞在下一次recv)
服务器必须先告知客户端文件的大小,因为客户端如果不知道需要接收多少数据,就不知道何时停止。 只有当连接关闭的时候,recv返回值才为0,而服务器不会主动断开连接
需要注意的是,当客户端socket关闭时,服务器再写入数据则会收到内核发送的SIGPIPE,导致服务器崩溃
解决方法可以是服务器发一次数据,然后接收一次数据,若recv返回值为0,则表示客户端关闭。缺点是需要发送更多的次数
获取文件的权限
struct stat
{
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; /* protection -保护模式?*/
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; /* user ID of owner -user id*/
gid_t st_gid; /* group ID of owner - group id*/
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
time_t st_atime; /* time of last access -最近存取时间*/
time_t st_mtime; /* time of last modification -最近修改时间*/
time_t st_ctime; /* time of last status change - */
};
struct stat file_info = {0};
stat(file_name, &file_info);
*mode = file_info.st_mode;
获取文件的大小的两种方式:
int get_size_1(char* file_name){
struct stat file_info = {0};
stat(file_name, &file_info);
return file_info.st_size;
}
int get_size_2(char* file_name){
int fp = open(file_name, O_RDONLY);
int size = lseek(fp, 0, SEEK_END);;
close(fp);
return size;
}
int fp = open(file_name, O_RDONLY);
// 将读写位置移到文件开头,并返回0
lseek(fp, 0, SEEK_SET);
// 将读写位置移到文件末尾,返回文件末尾的偏移量
int file_size = lseek(fp, 0, SEEK_END);
// 获取文件读写位置相对于开头的偏移量
int cur = lseek(fp, 0, SEEK_CUR);
断点续传实现思路
- 客户端下载的时候,在没下载完成前先创建一个临时的
.tmp文件,下载完成才将这个临时文件重命名成和服务器端相同的文件名。 - 下载文件的时候,客户端首先在本地目录查找是否有对应的临时文件,如果有则把这个文件的大小发送给服务器。服务器偏移对应的字节数发送即可,服务器把文件以追加的形式打开再次写入即可。
一个的设计问题:服务器和客户端之间的交互
服务器只有一台,且协议版本是不断升级的,然而客户端所用的版本可能不太统一,有的可能使用老的传输协议,有的使用新的传输协议。服务器升级后,再和老版本的客户端交互可能就会造成数据解析错误的问题。
解决方法可以是:客户端连接的时候就把自己的版本号发送给服务器,服务器根据客户端的版本号选择相应的函数与客户端进行交互,解析客户端的数据。
关于文件校验
客户端发送下载请求后,服务器除了发送文件的大小还需要发送该文件的MD5值给客户端,客户端下载完成后,计算自己本地文件的MD5,对比两个MD5值即可完成校验。然后给用户相应的提示即可。
关于秒传
为节省服务器磁盘空间以及节省带宽,客户端上传文件的时候,首先让客户端把该文件的加密串发给服务器,服务器校验这个加密串是否已经存在,若存在则可以给这个用户的账户新建一个快捷方式,告诉客户端已经上传完成。
一个用户上传一个文件后,若后面的用户也上传相同的文件,即可使用校验加密串以及新建快捷方式的方法实现秒传。若其中的用户想自己上传的删除文件,服务器删除快捷方式即可,源文件依然存在。若这个文件没人引用了,那么服务器可以删除源文件,或者保留一定时间后依然没人引用即可删除。
其实秒传的加密算法一般不使用MD5,因为MD5的加密串是32位的,如果服务器的文件数量超过 ,就肯定会出现MD5值冲突的情况
源代码仓库:github.com/BugMaker-sh…