需求
在Linux上利用流套接字提供客户端和服务端面向连接的交互。进行登陆验证、查看服务器列表、下载文件、远程执行命令的几个操作。
设计
- 每次请求客户端首先发出一个报头,得到服务端的响应后确保连接的正确建立,之后开始传文件。
- 对于下载文件操作:客户端输入一个相对路径或者绝对路径以及文件名,等到服务端响应一个确认报文后开始进行收文件。其中文件的传递利用二进制流的形式发送,这样的优点是能够将文件分成许多块发送且能够方便客户端读取。
- 对于执行命令操作服务端通过客户端传来的命令,将其切割成各个命令以及参数块,之后利用fork以及exec函数族的形式让子进程执行改命令。要注意的是,通过exec执行的命令很难与其他命令嵌套,比如我们要把操作结果返回给客户端需要用到文件重定向,这里需要使用system函数,其内部实现是exec+fork+文件;能够轻易把传来的命令正确执行最后将结果重定向到文件中reply给客户端。
报文设计:
实现
服务端:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h> // 提供属性操作函数
#include <sys/types.h> // 提供mode_t 类型
#include <malloc.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h> // 提供目录流操作函数
#define PORT 56193
#define MAX_UNAME 8
#define MAX_PASSWD 8
#define MAX_BUF_SZ 1024
#define MAX_CMD_SZ 20
#define TYPE_LOGIN 1
#define TYPE_READ 2
#define TYPE_EXIT 3
#define TYPE_EXEC_CMD 4
#define TYPE_GET_FILE 5
#define TYPE_NOT_FILE 6
#define TYPE_YES_FILE 7
struct hdr_st{
unsigned short type;
unsigned short len;
};
void scan_dir(char *dir, char *strbuf, int bufsz) // 定义目录扫描函数
{
DIR *dp; // 定义子目录流指针
struct dirent *entry; // 定义dirent结构指针保存后续目录
struct stat statbuf; // 定义statbuf结构保存文件属性
int sz, remain = bufsz;
char *pos = strbuf;
if((dp = opendir(dir)) == NULL) // 打开目录,获取子目录流指针,判断操作是否成功
{
puts("can't open dir.");
return;
}
chdir (dir); // 切换到当前目录
while((entry = readdir(dp)) != NULL) // 获取下一级目录信息,如果未否则循环
{
lstat(entry->d_name, &statbuf); // 获取下一级成员属性
if(S_IFDIR &statbuf.st_mode) // 判断下一级成员是否是目录
{
if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
continue;
}
sz = strlen(entry->d_name);
if(sz < remain){
remain -= sz;
sprintf(pos, "%s\n", entry->d_name); // 输出属性不是目录的成员
pos += (sz+1);
}else return ;
}
chdir(".."); // 回到上级目录
closedir(dp); // 关闭子目录流
}
void split(char cmd_list[][MAX_CMD_SZ], char *cmd, int *sz) {
int i, j, len, flag = 0;
len = strlen(cmd);
*sz = 0;
j = 0;
for (i = 0; i < len; i++) {
if (cmd[i] == ' ' && i && cmd[i-1] != ' ') {
cmd_list[*sz][j] = 0;
*sz = *sz + 1;
j = 0;
flag = 0;
} else if (cmd[i] != ' ') {
cmd_list[*sz][j++] = cmd[i];
flag = 1;
}
}
if (flag) {
cmd_list[*sz][j] = 0;
*sz = *sz + 1;
}
}
int do_login(int sAccept)
{
char usrname[MAX_UNAME];
char passwd[MAX_PASSWD];
int sz;
printf("Receive Login request!\n");
/* TODO here do not handle the sending delay */
sz = recv(sAccept, usrname, MAX_UNAME, 0);
if(sz < 0){
printf("Did not receive the enough Message in login.\n");
return 0;
}
sz = recv(sAccept, passwd, MAX_UNAME, 0);
if(sz < 0){
printf("Did not receive the enough Message in login.\n");
return 0;
}
if (strcmp(usrname, "root") != 0 || strcmp(passwd, "123456") != 0) {
return 0;
}
printf("User name :%s\n", usrname);
printf("passwd: %s\n", passwd);
return 1;
}
void do_get_file_list(int sockfd) {
char file_list[MAX_BUF_SZ];
scan_dir(".", file_list, MAX_BUF_SZ - 1);
if ( send(sockfd, &file_list, sizeof(file_list), 0) < 0