前言
文件传输是计算机网络的基本功能,文件传输协议(File Transfer Protocol,FTP)是一个基本的应用层协议。
FTP 是 File Transfer Protocol 的简称,即文件传输协议的缩写。该协议用于在两台计算机之间传送文件。FTP 会话包含了两个通道,一个是控制通道,一个是数据通道。控制通道是和 FTP 服务器进行沟通的通道,连接 FTP 服务器,发送 FTP 指令;数据通道则是和 FTP服务器进行文件传输或者获取文件列表的通道。FTP 中,控制连接的各种指令均由客户端主动发起,而数据连接有两种工作方式:主动方式(PORT 方式)和被动方式(PASV 方式)。主动方式下,FTP 客户端首先和 FTP 服务器的控制通道对应端口(一般为 21)建立连接,通过控制通道发送命令,客户端需要接收数据的时候在这个通道上发送 PORT 命令。PORT 命令包含了客户端用什么端口(一个大于 1024的端口)接收数据。在传输数据的时候,FTP 服务器必须和客户端建立一个新的连接。被动方式下,建立控制通道的过程和主动方式类似,当客户端通过这个通道发送 PASV 命令的时候,FTP Server 打开一个位于 1024~5000 之间的随机端口并且通知客户端,然后客户端与服务器之间将通过这个端口进行数据的传送。具体的 FTP 规范请参考 RFC959。
需求分析
设计目的:
用高级语言编写和调试一个简单的 FTP 服务系统,掌握对进程、线程、同 步、通信、文件系统及网络编程的方法。从而加深学生对远程服务机制的理解和认识。
设计要求:
- 该系统要求有服务器端软件和客户端软件两部分组成,服务器端在指定端口接受客户连接 请求,根据客户要求执行相应处理,客户端提供系统的交互界面
- 基于套接字得客户、服务器通信模式
- 远程登录功能:要求集成 linux 系统的客户管理功能,对客户信息予以验证。
- 并发执行及管理功能:采用多线程,客户通过身份请求后创建一个新线程来响应客户请求。
- 文件管理功能 :服务器端的文件基本操作包括:mkdir、rmdir、cd、ls 、pwd
- 客户端执行的基本操作包括:创建及删除目录、切换目录、查看当前目录下的所有文 件
- 文件传输:upload/download 到指定目录。
开发环境
本程序分为服务器端和客户端两部分,全部在Ubuntu系统中使用 sublime编辑器配合GCC 编译执行。
程序特点
本程序主要使用套接口函数实现服务器端/客户端通信,具有较强的安全性设计和错误。采用多线程设计,可以保证多个客户端登陆同一服务器端而不冲突。 程序使用命令提示符界面,在指定账户登录可实现全部功能,而在匿名账户下可实现部 分功能。
总体设计
功能模块
本程序分为套接口通信模块、登录模块、命令解析模块、文件操作模块、文件传输模块 共 5 个模块,具体如下:
服务器客户端通信模块
序号 | 函数定义 | 功能 |
---|---|---|
1 | void handle_pasv(int client_sock, struct sockaddr_in client) | 设定ftp工作模式 |
2 | void *Handle_Client_Request(void *arg) | 响应客户端请求 |
3 | void do_client_work(int client_sock, struct sockaddr_in client) | 处理客户端工作 |
4 | void send_client_info(int client_sock, char* info, int length); | 发送客户端信息 |
5 | int recv_client_info(int client_sock); | 接收客户端信息 |
6 | int fill_host_addr(char * host_ip_addr, struct sockaddr_in * host, int port) | 写入服务器地址 |
7 | int ftp_get_reply(int sock_fd) | 接收服务端回应 |
8 | int ftp_send_cmd(const char *s1, const char *s2, int sock_fd) | 发送ftp命令 |
9 | int xconnect(struct sockaddr_in * s_addr, int type) | 连接服务器与客户端 |
10 | int xconnect_ftpdata() | 传输数据 |
11 | struct sockaddr_in create_date_sock() | 创建数据管道 |
登录模块
序号 | 函数定义 | 功能 |
---|---|---|
1 | int ftp_login() | 登录到ftp服务器 |
2 | int login() | 客户端登录 |
3 | void get_user() | 获取用户名 |
4 | void get_pass() | 获取密码 |
5 | void close_cli() | 关闭客户端连接 |
6 | void show_help() | 显示帮助信息 |
7 | void ftp_quit() | 退出客户端 |
命令解析模块
序号 | 函数定义 | 功能 |
---|---|---|
1 | int ftp_user_cmd(char * user_cmd) | 解析用户输入的命令 |
2 | int start_ftp_cmd(char * host_ip_addr, int port) | 连接到服务器的命令 |
3 | void cmd_err_exit(char * err_msg, int err_code) | 处理错误的命令 |
4 | void ftp_cmd_filename(char * user_cmd, char * src_file, char * dst_file) | 处理文件名的命令 |
文件传输模块
序号 | 函数定义 | 功能 |
---|---|---|
1 | void ftp_put(char * user_cmd) | 上传文件 |
2 | void ftp_get(char * user_cmd) | 下载文件 |
3 | void handle_file(int client_sock) | 检查文件 |
4 | void handle_del(int client_sock) | 删除文件 |
文件操作模块
序号 | 函数名 | 功能 |
---|---|---|
1 | void handle_cwd(int client_sock) | 处理转换目录的请求 |
2 | void handle_rmd(int client_sock) | 处理删除目录的请求 |
3 | void handle_mkd(int client_sock) | 处理新建文件夹的请求 |
4 | void handle_list(int client_sock) | 处理列出文件列表的请求 |
5 | void ftp_list() | 列出服务器文件列表 |
6 | void ftp_pwd() | 显示服务器当前所在目录 |
7 | void ftp_cd() | 转到服务器指定目录 |
8 | void del() | 删除文件 |
9 | void mkdir_srv() | 新建文件夹 |
10 | void rmdir_srv() | 删除文件夹 |
11 | void local_list() | 列出客户端文件列表 |
12 | void local_pwd() | 显示客户端当前所在目录 |
13 | void local_cd() | 转到客户端指定目录 |
程序流程图
详细实现方法
获取用户名密码
相关代码
//获取用户名
void get_user(){
char read_buf[64];
printf("User(Press for anonymous): ");
fgets(read_buf, sizeof(read_buf), stdin);
if(read_buf[0] == ‘\n‘)
strncpy(user, "anonymous", 9);
else
strncpy(user, read_buf, strlen(read_buf) - 1);
}
//获取密码
void get_pass(){
char read_buf[64];
printf("Password(Press for anonymous): ");
echo_off();
fgets(read_buf, sizeof(read_buf), stdin);
if(read_buf[0] == ‘\n‘)
strncpy(passwd, "anonymous", 9);
else
strncpy(passwd, read_buf, strlen(read_buf) - 1);
echo_on();
printf("\n");
}
//登录ftp
int ftp_login(){
int err;
get_user();
if(ftp_send_cmd("USER ", user, sock_control) < 0)
cmd_err_exit("Can not send message", 1);
err = ftp_get_reply(sock_control);
if(err == 331){
get_pass();
if(ftp_send_cmd("PASS ", passwd, sock_control) <= 0)
cmd_err_exit("Can not send message", 1);
else
err = ftp_get_reply(sock_control);
if(err == 230)
return 1;
else if(err == 531)
return 1;
else{
printf("Password error!\n");
return 0;
}
}
else{
printf("User error!\n");
return 0;
}
}
测试结果
连接服务器
相关代码
//连接到服务器
int xconnect(struct sockaddr_in * s_addr, int type){
struct timeval outtime;
int set;
int s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0){
cmd_err_exit("Create socket error!", 249);
}
if(type == 1){
outtime.tv_sec = 0;
outtime.tv_usec = 300000;
}
else{
outtime.tv_sec = 5;
outtime.tv_usec = 0;
}
set = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &outtime, sizeof(outtime));
if(set != 0){
printf("Set socket %s errno:%d\n", strerror(errno), errno);
cmd_err_exit("set socket", 1);
}
//连接到服务器,如果连接成功,connect函数返回0,如果不成功,返回-1。
if(connect(s, (struct sockaddr *)s_addr, sizeof(struct sockaddr_in)) < 0){
printf("Can‘t connect to server %s, prot %d\n", inet_ntoa(s_addr -> sin_addr), ntohs(ftp_server.sin_port));
exit(252);
}
return s;
}
//发送 FTP 指令
int ftp_send_cmd(const char *s1, const char *s2, int sock_fd){
char send_buf[256];
int send_err, len;
if(s1){
strcpy(send_buf, s1);
if(s2){
strcat(send_buf, s2);
strcat(send_buf, "\r\n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
else{
strcat(send_buf, "\r\n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
}
if(send_err < 0){
printf("Send() error!\n");
}
return send_err;
}
//接收服务器返回信息
int ftp_get_reply(int sock_fd){
static int reply_code = 0, count = 0;
char rcv_buf[512];
count = read(sock_fd, rcv_buf, 510);
if(count > 0){
reply_code = atoi(rcv_buf);
}
else{
return 0;
}
while(1){
if(count <= 0){
break;
}
rcv_buf[count] = ‘\0‘;
printf("%s\n", rcv_buf);
count = read(sock_fd, rcv_buf, 510);
}
return reply_code;
}
int get_port(){
char port_respond[512];
char *buf_ptr;
int count, port_num;
ftp_send_cmd("PASV", NULL, sock_control);
count = read(sock_control, port_respond, 510);
if(count <= 0){
return 0;
}
port_respond[count] = ‘\0‘;
if(atoi(port_respond) == 227){
buf_ptr = strrchr(port_respond, ‘,‘);
port_num = atoi(buf_ptr + 1);
*buf_ptr = ‘\0‘;
buf_ptr = strrchr(port_respond, ‘,‘);
port_num += atoi(buf_ptr + 1) * 256;
return port_num;
}
return 0;
}
int rand_local_port(){
int local_port;
srand((unsigned)time(NULL));
local_port = rand() % 40000 + 1025; //0~1024是系统保留端口号
return local_port;
}
测试结果
连接服务器数据流
相关代码
//连接服务器数据流
int xconnect_ftpdata(){
//PASV
if(mode){
int data_port = get_port();
if(data_port != 0)
ftp_server.sin_port = htons(data_port);
return(xconnect(&ftp_server, 0));
}
//PORT
else{
int client_port, get_sock, opt, set;
char cmd_buf[32];
struct timeval outtime;
struct sockaddr_in local;
char local_ip[24];
char *ip_1, *ip_2, *ip_3, *ip_4;
int addr_len = sizeof(struct sockaddr);
client_port = rand_local_port();
get_sock = socket(AF_INET, SOCK_STREAM, 0);
if(get_sock < 0){
cmd_err_exit("socket()", 1);
}
outtime.tv_sec = 7;
outtime.tv_usec = 0;
opt = SO_REUSEADDR;
set = setsockopt(get_sock, SOL_SOCKET, SO_RCVTIMEO, &outtime, sizeof(outtime));
if(set != 0){
printf("Set socket %s errno:%d\n", strerror(errno), errno);
cmd_err_exit("set socket", 1);
}
set = setsockopt(get_sock, SOL_SOCKET, SO_REUSEADDR, &opt,sizeof(opt));
if(set !=0)
{
printf("set socket %s errno:%d\n",strerror(errno),errno);
cmd_err_exit("set socket", 1);
}
bzero(&local_host, sizeof(local_host));
local_host.sin_family = AF_INET;
local_host.sin_port = htons(client_port);;
local_host.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&local, sizeof(struct sockaddr));
while(1){
set = bind(get_sock, (struct sockaddr *)& local_host, sizeof(local_host));
if(set != 0 && errno == 11){
client_port = rand_local_port();
continue;
}
set = listen(get_sock, 1);
if(set != 0 && errno == 11){
cmd_err_exit("listen()", 1);
}
//获取ip地址
if(getsockname(sock_control, (struct sockaddr *)&local, (socklen_t *)&addr_len) < 0)
return -1;
snprintf(local_ip, sizeof(local_ip), inet_ntoa(local.sin_addr));
local_ip[strlen(local_ip)] = ‘\0‘;
ip_1 = local_ip;
ip_2 = strchr(local_ip, ‘.‘);
*ip_2 = ‘\0‘;
ip_2++;
ip_3 = strchr(ip_2, ‘.‘);
*ip_3 = ‘\0‘;
ip_3++;
ip_4 = strchr(ip_3, ‘.‘);
*ip_4 = ‘\0‘;
ip_4++;
snprintf(cmd_buf, sizeof(cmd_buf), "PORT %s, %s, %s, %s, %d, %d", ip_1, ip_2, ip_3, ip_4, client_port >> 8, client_port & 0xff);
ftp_send_cmd(cmd_buf, NULL, sock_control);
if(ftp_get_reply(sock_control) != 200){
printf("Can not use PORT mode! Please use \"mode\" change to PASV mode.\n");
return -1;
}
else
return get_sock;
}
}
}
向服务器发送客户端命令
相关代码
//发送 FTP 指令
int ftp_send_cmd(const char *s1, const char *s2, int sock_fd){
char send_buf[256];
int send_err, len;
if(s1){
strcpy(send_buf, s1);
if(s2){
strcat(send_buf, s2);
strcat(send_buf, "\r\n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
else{
strcat(send_buf, "\r\n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
}
if(send_err < 0){
printf("Send() error!\n");
}
return send_err;
}
接收服务器返回信息
相关代码
//接收服务器返回信息
int ftp_get_reply(int sock_fd){
static int reply_code = 0, count = 0;
char rcv_buf[512];
count = read(sock_fd, rcv_buf, 510);
if(count > 0){
reply_code = atoi(rcv_buf);
}
else{
return 0;
}
while(1){
if(count <= 0){
break;
}
rcv_buf[count] = ‘\0‘;
printf("%s\n", rcv_buf);
count = read(sock_fd, rcv_buf, 510);
}
return reply_code;
}
显示服务器当前目录下文件
相关代码
//显示服务器当前目录下文件
void ftp_list(){
int i = 0, new_sock;
int set = sizeof(local_host);
int list_sock_data = xconnect_ftpdata();
if(list_sock_data < 0){
ftp_get_reply(sock_control);
printf("Create data sock error!\n");
return;
}
ftp_get_reply(sock_control);
ftp_send_cmd("LIST", NULL, sock_control);
ftp_get_reply(sock_control);
if(mode)
//PASV
ftp_get_reply(list_sock_data);
else{
//PORT
while(i < 3){
new_sock = accept(list_sock_data, (struct sockaddr *)& local_host, (socklen_t *)& set);
if(new_sock == -1){
printf("Accept return:%s errno:%d\n", strerror(errno), errno);
i++;
continue;
}
else
break;
}
if(new_sock == -1){
printf("Sorry, you can not use PORT mode. There is something wrong when the server connect to you.\n");
return;
}
ftp_get_reply(new_sock);
close(new_sock);
}
close(list_sock_data);
ftp_get_reply(sock_control);
}
测试结果
列出客户端当前目录下文件
相关代码
void local_list(){
DIR * dp;
struct dirent * dirp;
if((dp = opendir("./")) == NULL){
printf("Opendir() error!\n");
return;
}
printf("Local file list:\n");
while((dirp = readdir(dp)) != NULL){
if(strcmp(dirp -> d_name, ".") == 0 || strcmp(dirp -> d_name, "..") == 0){
continue;
}
printf("%s\n", dirp -> d_name);
}
}
测试结果
显示客户端目录
相关代码
void local_pwd(){
char curr_dir[512];
int size = sizeof(curr_dir);
//得到客户端目录
if(getcwd(curr_dir, size) == NULL)
printf("Getcwd failed\n");
else
printf("Current local directory: %s\n", curr_dir);
}
测试结果
处理客户端需求
相关代码
//处理客户需求,从客户端连接成功开始到结束服务
void *Handle_Client_Request(void *arg){
struct ARG*info;
info = (struct ARG*)arg;
printf("You got a connection from %s\n", inet_ntoa(info->client.sin_addr));
do_client_work(info -> client_sock, info -> client);
close(info -> client_sock);
pthread_exit(NULL);
}
处理FTP各种命令
相关代码
// 与客户端交互
void do_client_work(int client_sock, struct sockaddr_in client){
int login_flag;
login_flag = login(client_sock);
while(recv_client_info(client_sock) && login_flag == 1){
if((strncmp("quit", client_Control_Info, 4) == 0) || (strncmp("QUIT", client_Control_Info, 4) == 0)){
send_client_info(client_sock, serverInfo221, strlen(serverInfo221));
break;
}
else if((strncmp("close", client_Control_Info, 5) == 0) || (strncmp("CLOSE", client_Control_Info, 5) == 0)){
printf("Client quit!\n");
shutdown(client_sock, SHUT_WR); // 断开输出流
}
else if((strncmp("pwd", client_Control_Info, 3) == 0) || (strncmp("PWD", client_Control_Info, 3) == 0)){
char pwd_info[MSG_INFO];
char tmp_dir[DIR_INFO];
snprintf(pwd_info, MSG_INFO, "257 \"%s\" is current location. \r\n", getcwd(tmp_dir, DIR_INFO));
send_client_info(client_sock, pwd_info, strlen(pwd_info));
}
else if((strncmp("cwd", client_Control_Info, 3) == 0) || (strncmp("CWD", client_Control_Info, 3) == 0)){
handle_cwd(client_sock);
}
else if((strncmp("mkd", client_Control_Info, 3) == 0) || (strncmp("MKD", client_Control_Info, 3) == 0)){
handle_mkd(client_sock);
}
else if((strncmp("rmd", client_Control_Info, 3) == 0) || (strncmp("RMD", client_Control_Info, 3) == 0)){
handle_rmd(client_sock);
}
else if((strncmp("dele", client_Control_Info, 4) == 0) || (strncmp("DELE", client_Control_Info, 4) == 0)){
handle_del(client_sock);
}
else if((strncmp("pasv", client_Control_Info, 4) == 0) || (strncmp("PASV", client_Control_Info, 4) == 0)){
handle_pasv(client_sock, client);
}
else if((strncmp("list", client_Control_Info, 4) == 0) || (strncmp("LIST", client_Control_Info, 4) == 0)){
handle_list(client_sock);
send_client_info(client_sock, serverInfo226, strlen(serverInfo226));
}
else if((strncmp("type", client_Control_Info, 4) == 0) || (strncmp("TYPE", client_Control_Info, 4) == 0)){
if((strncmp("type I", client_Control_Info, 6) == 0) ||(strncmp("TYPE I", client_Control_Info, 6) == 0)){
translate_data_mode = FILE_TRANS_MODE_BIN;
}
send_client_info(client_sock, serverInfo200, strlen(serverInfo200));
}
else if((strncmp("retr", client_Control_Info, 4) == 0)||(strncmp("RETR", client_Control_Info, 4) == 0)){
handle_file(client_sock);
send_client_info(client_sock, serverInfo226, strlen(serverInfo226));
}
else if(strncmp("stor", client_Control_Info, 4) == 0||(strncmp("STOR", client_Control_Info, 4) == 0)){
handle_file(client_sock);
send_client_info(client_sock,serverInfo226, strlen(serverInfo226));
}
else if(strncmp("syst", client_Control_Info, 4) == 0||(strncmp("SYST", client_Control_Info, 4) == 0)){
send_client_info(client_sock, serverInfo215, strlen(serverInfo215));
}
else if(strncmp("size", client_Control_Info, 4) == 0||(strncmp("SIZE", client_Control_Info, 4) == 0)){
send_client_info(client_sock, serverInfo213, strlen(serverInfo213));
}
else if(strncmp("feat", client_Control_Info, 4) == 0||(strncmp("FEAT", client_Control_Info, 4) == 0)){
send_client_info(client_sock, serverInfo211, strlen(serverInfo211));
}
else if(strncmp("rest", client_Control_Info, 4) == 0||(strncmp("REST", client_Control_Info, 4) == 0)){
send_client_info(client_sock, serverInfo350, strlen(serverInfo350));
}
else {
send_client_info(client_sock, serverInfo, strlen(serverInfo));
}
}
while(recv_client_info(client_sock) && (login_flag == 2)){
if((strncmp("quit", client_Control_Info, 4) == 0)||(strncmp("QUIT", client_Control_Info, 4) ==0)){
send_client_info(client_sock, serverInfo221, strlen(serverInfo221));
break;
}
else if((strncmp("close",client_Control_Info, 5) == 0)||(strncmp("CLOSE",client_Control_Info, 5) == 0)){
printf("Client Quit!\n");
shutdown(client_sock, SHUT_WR); // 断开输出流
}
else if((strncmp("pwd", client_Control_Info, 3) == 0)||(strncmp("PWD", client_Control_Info, 3) == 0)){
char pwd_info[MSG_INFO];
char tmp_dir[DIR_INFO];
snprintf(pwd_info, MSG_INFO, "257 \"%s\" is current location.\r\n", getcwd(tmp_dir, DIR_INFO));
send_client_info(client_sock, pwd_info, strlen(pwd_info));
}
else if((strncmp("cwd", client_Control_Info, 3) == 0)||(strncmp("CWD", client_Control_Info, 3) == 0)){
handle_cwd(client_sock);
}
else if((strncmp("pasv", client_Control_Info, 4) == 0)||(strncmp("PASV", client_Control_Info, 4)== 0)){
handle_pasv(client_sock,client);
}
else if((strncmp("list", client_Control_Info, 4) == 0)||(strncmp("LIST", client_Control_Info, 4)== 0)){
handle_list(client_sock);
send_client_info(client_sock, serverInfo226, strlen(serverInfo226));
}
else if((strncmp("type", client_Control_Info, 4) == 0)||(strncmp("TYPE", client_Control_Info, 4)== 0)){
if((strncmp("type I", client_Control_Info, 6) == 0)||(strncmp("TYPE I", client_Control_Info, 6) == 0)) {
translate_data_mode = FILE_TRANS_MODE_BIN;
}
send_client_info(client_sock, serverInfo200, strlen(serverInfo200));
}
else if((strncmp("retr", client_Control_Info, 4) == 0)||(strncmp("RETR", client_Control_Info, 4)== 0)){
handle_file(client_sock);
send_client_info(client_sock,serverInfo226, strlen(serverInfo226));
}
else if((strncmp("syst", client_Control_Info, 4) == 0)||(strncmp("SYST", client_Control_Info, 4)== 0)){
send_client_info(client_sock, serverInfo215, strlen(serverInfo215));
}
else if((strncmp("size", client_Control_Info, 4) == 0)||(strncmp("SIZE", client_Control_Info, 4)== 0)){
send_client_info(client_sock, serverInfo213, strlen(serverInfo213));
}
else if((strncmp("feat", client_Control_Info, 4) == 0)||(strncmp("FEAT", client_Control_Info, 4)== 0)){
send_client_info(client_sock, serverInfo211, strlen(serverInfo211));
}
else if((strncmp("rest", client_Control_Info, 4) == 0)||(strncmp("REST", client_Control_Info, 4)== 0)){
send_client_info(client_sock, serverInfo350, strlen(serverInfo350));
}
else{
send_client_info(client_sock, serverInfo, strlen(serverInfo));
}
}
}
创建数据sock
相关代码
//)创建数据sock。
struct sockaddr_in create_date_sock(){
int t_client_sock;
struct sockaddr_in t_data_addr;
t_client_sock = socket(AF_INET, SOCK_STREAM, 0);
if(t_client_sock < 0){
printf("Creating data socket error!\n");
return;
}
srand((int)time(0));
int a = rand() % 1000 + 1025;
bzero(&t_data_addr, sizeof(t_data_addr));
t_data_addr.sin_family = AF_INET;
t_data_addr.sin_addr.s_addr = htonl(INADDR_ANY);
t_data_addr.sin_port = htons(a);
if(bind(t_client_sock, (struct sockaddr *)&t_data_addr, sizeof(struct sockaddr)) < 0){
printf("Bind error in create data socket:%s\n", strerror(errno));
return;
}
listen(t_client_sock, LISTEN_QENU);
ftp_data_sock = t_client_sock;
return t_data_addr;
}
服务器处理文件
相关代码
// 处理文件类命令,把上传下载功能集成在一个函数中实现
void handle_file(int client_sock){
send_client_info(client_sock, serverInfo150, strlen(serverInfo150));
int t_data_sock;
struct sockaddr_in client;
int sin_size = sizeof(struct sockaddr_in);
if((t_data_sock = accept(ftp_data_sock, (struct sockaddr *)&client, &sin_size)) == -1){
perror("Accept error");
return;
}
int i = 0;
int length = strlen(client_Control_Info);
//格式化命令
for(i = 5; i < length; i++){
format_client_Info[i - 5] = client_Control_Info[i];
}
format_client_Info[i - 7] = ‘\0‘;
FILE* fp; //建立管道处理文件信息
int file_fd;
int n;
char t_dir[DIR_INFO];
char file_info[DIR_INFO];
snprintf(file_info, DIR_INFO, "%s/%s", getcwd(t_dir, DIR_INFO), format_client_Info); //获取文件信息
char file_mode[3];
if((strncmp("retr", client_Control_Info, 4) == 0) || (strncmp("RETR", client_Control_Info, 4) == 0)){
//打开文件方式
file_mode[0] = ‘r‘;
file_mode[1] = ‘b‘;
file_mode[2] = ‘\0‘;
}
else{
file_mode[0] = ‘a‘;
file_mode[1] = ‘b‘;
file_mode[2] = ‘\0‘;
}
if(strncmp(getcwd(t_dir, DIR_INFO), format_client_Info, strlen(getcwd(t_dir, DIR_INFO)) - 1) == 0){
fp = fopen(format_client_Info, file_mode); //打开文件
}
else{
fp = fopen(file_info, file_mode);
}
if(fp == NULL){
printf("Open file error: %s\r\n", strerror(errno));
char cwd_info[MSG_INFO];
snprintf(cwd_info, MSG_INFO, "550 %s : %s\r\n", format_client_Info, strerror(errno));
send_client_info(client_sock, cwd_info,strlen(cwd_info));
close(t_data_sock);
close(ftp_data_sock);
return;
}
int cmd_sock = fileno(fp); //获取文件描述词
memset(client_Data_Info, 0, MSG_INFO);
//下载文件
if((strncmp("retr", client_Control_Info, 4) == 0) || (strncmp("RETR", client_Control_Info, 4) == 0)){
while((n = read(cmd_sock, client_Data_Info, MSG_INFO)) > 0){ //读写文件
if(write(t_data_sock, client_Data_Info, n) != n){
printf("Retr transfer error!\n");
return;
}
}
}
//上传文件
else{
while((n = read(t_data_sock, client_Data_Info, MAX_INFO)) > 0){
if(write(cmd_sock, client_Data_Info, n) != n){
printf("Stor transfer error!\n");
return;
}
}
}
fclose(fp); //关闭传输管道
close(t_data_sock);
close(ftp_data_sock);
}
发送、接收客户端信息
相关代码
//发送客户端信息。
void send_client_info(int client_sock, char* info, int length){
int len;
if((len = send(client_sock, info, length, 0)) < 0){
perror("Send info error!");
return;
}
}
//接收客户端信息。
int recv_client_info(int client_sock){
int num;
if((num = recv(client_sock, client_Control_Info, MSG_INFO, 0)) < 0){
perror("Receive info error!");
return;
}
client_Control_Info[num] = ‘\0‘;
printf("Client %ld Message: %s\n", pthread_self(), client_Control_Info);
if((strncmp("USER", client_Control_Info, 4) == 0) || (strncmp("user", client_Control_Info, 4) == 0)){
return 2;
}
return 1;
}
P.S.
多客户端同时登录
采用多线程设计,可以保证多个客户端登陆同一服务器端而不冲突。
相关代码
while(1){
if((ftp_client_sock = accept(ftp_server_sock, (struct sockaddr *)&client, &sin_size)) == -1){
perror("Accept error!");
exit(1);
}
arg.client_sock = ftp_client_sock;
memcpy((void*)&arg.client, &client, sizeof(client));
if(pthread_create(&thread, NULL, Handle_Client_Request, (void*)&arg)){
perror("Thread create error!");
exit(1);
}
}
测试结果
FTP规范
相关代码
char serverInfo220[]="220 myFTP Server ready...\r\n";
char serverInfo230[]="230 User logged in, proceed.\r\n";
char serverInfo331[]="331 User name okay, need password.\r\n";
char serverInfo221[]="221 Goodbye!\r\n";
char serverInfo213[]="213 File status.\r\n";
char serverInfo211[]="211 System status, or system help reply.\r\n";
char serverInfo350[]="350 Requested file action pending further information.\r\n";
char serverInfo530[]="530 Not logged in.\r\n";
char serverInfo531[]="531 Not root client. Anonymous client.\r\n";
char serverInfo[]="202 Command not implemented, superfluous at this site.\r\n";
char serverInfo150[]="150 File status okay; about to open data connection.\r\n";
char serverInfo226[]="226 Closing data connection.\r\n";
char serverInfo200[]="200 Command okay.\r\n";
char serverInfo215[]="215 Unix Type FC5.\r\n";