目录 文件传输协议的简单设计与实现 0 一、 设计目的 0 二、 设计内容和功能 0 1、设计内容 0 2、具体功能 0 三、设计平台与语言 1 四、 设计具体步骤 1 1、 显示当前IP(QT的接口函数实现) 9 2、 当前客户连接数 9 3、 可查看连接客户的ip信息 9 4、 消息提示 9 5、 显示当前工作路径和当前目录的内容 10 6、 可查看服务器磁盘的所有文件 10 7、改变当前工作目录。 10 7、 查看已连接客户的IP 11 1、 命令解析 11 2、 帮助命令”?” 11 六、 课程设计心得 12 七、 参考文献 12 二、设计内容和功能 1、设计内容 我们的计算机网络实验环境建立在TCP/IP 网络体系结构之上。各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。实验室各计算机具备Windows环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。本设计实现了一个简单的文件传送协议。 2、具体功能 用socket 编程接口编写两个程序,分别为客户程序和服务器程序,该程序应能实现了下述命令功能: get:取远方的一个文件 put:传给远方一个文件 pwd:显示远主当前目录 dir:列出远方当前目录 cd :改变远方当前目录 ? :显示你提供的命令 ls :列出当前目录 quit :退出返回 其中支持多连接,并限制了只能有三个并发客户端。 三、设计平台与语言 平台:LINUX 语言:C 和 C++ 界面设计: qt 四、设计具体步骤 ⑴、总体方案设计:
服务器: 服务器中一直在阻塞地等待客户端的连接。而每个连接一个客户端就会创建一一条TCP通道,并用一线程和处理这条TCP通道的命令传输。每当需要进行文件传输时,就会再创建一条TCP通道进行文件传输,传输完毕后释放这条通道。 客户端:首先通过TCP连接到服务器产生一条TCP通道,并创建一线程来处理服务发来的信息,然后通过服务器提供的命令和服务器进行交互,当需要获取或上传文件时,再创建一条TCP通道进行文件传输,传输完毕后释放这条通道,且可以保持和服务器进行交互。
#include "ftp_client.h"
/*******************************************************
*功能:
* get:取远方的一个文件
* put:传给远方一个文件
* pwd:显示远主当前目录
* dir:列出远方当前目录
* cd :改变远方当前目录
* ? :显示你提供的命令
* ls :显示本地目录
* quit:退出返回
*
*********************************************************/
/******************上传文件到服务器*******************/
int put_file(char *file)
{
int ffd; //file 文件描述符
char buf[256]; //缓存区
int total_c; //文件总大小
int sent_c = 0; //已经发送的字节数
int snd_c; //发送字节数
int ret; //处理返回值
int ack; //信号确认
/*打开要发送的文件*/
ffd = open(file, O_RDONLY);
if(ffd == -1)
{
printf("open %s failled\n", file);
goto SNDERR;
}
/*发送READY_T的信号*/
ack = READY_T;
ret = send(datafd, &ack, 4, 0);
/*等待对方发送确认的应答*/
ret = recv(datafd, &ack, 4, MSG_WAITALL);
if(ack == FAILL_T)
goto SNDERR;
/*发送文件总大小*/
total_c = lseek(ffd, 0, SEEK_END);
lseek(ffd, 0, SEEK_SET);
printf("have %.2f KB to send\n", (float)total_c/1024);
ret = send(datafd, &total_c, sizeof(total_c), 0);
/*开始发送文件*/
printf("sending %s .......\n", file);
snd_c = 255;
while(1)
{
bzero(buf, sizeof(buf));
//从文件中读取snd_c字节到缓存
ret = read(ffd, &buf, snd_c);
//发送snd_c个字节
ret = send(datafd, buf, snd_c, 0);
//总字节数-已发送字节数
total_c -= ret;
sent_c += ret;
if(total_c < snd_c)
snd_c = total_c;
if(total_c 0)
{
//把接收到的数据写入文件
write(ffd, buf, ret);
//总大小-接收到的字节数
file_c -= ret;
//更新收到的总数
got_c += ret;
}
else if(ret == 0)
{
printf("download %s success\n", file);
break;
}
else if(ret == -1)
{
printf("internet err!\n");
break;
}
}
/*等待发送完毕确认信号*/
ret = recv(datafd, &ack, 4, 0);
ack = OK_T;
/*发送以接收完毕确认信号*/
ret = send(datafd, &ack, 4, 0);
printf("download %.2f KB\n", (float)got_c/1024);
close(ffd);
return 0;
//错误处理
RECVERR:
//打印错误信息
perror("download err");
//发送接收错误信号
ack = FAILL_T;
ret = send(datafd, &ack, 4, 0);
//关闭文件
close(ffd);
return -1;
}
/******************显示帮助信息*******************/
void show_help()
{
printf("================帮助命令==================\n");
printf(" ==get 下载文件==\n ==put 上传文件== \
\n ==dir 列出远方当前目录==\n ==pwd 显示远主当前目录==\
\n ==cd 列出远方当前目录==\n ==? 显示命令帮助==\
\n ==ls 列出本地目录==\n ==quit 退出==\n");
printf("==========================================\n");
}
/******************解释命令*******************/
int get_cmd(char *msg)
{
int cmd = -1;
if(strncmp(msg, "get", 3) == 0 || strncmp(msg, "GET", 3) == 0) cmd = GET;
else if(strncmp(msg, "put", 3) == 0 || strncmp(msg, "PUT", 3) == 0) cmd = PUT;
else if(strncmp(msg, "pwd", 3) == 0 || strncmp(msg, "PWD", 3) == 0) cmd = PWD;
else if(strncmp(msg, "dir", 3) == 0 || strncmp(msg, "DIR", 3) == 0) cmd = GDIR;
else if(strncmp(msg, "cd", 2) == 0 || strncmp(msg, "cd", 2) == 0) cmd = CD;
else if(strncmp(msg, "?", 1) == 0 ) cmd = HELP;
else if(strncmp(msg, "ls", 2) == 0 || strncmp(msg, "LS", 2) == 0) cmd = LS;
else if(strncmp(msg, "quit",4) == 0 || strncmp(msg, "QUIT", 4) == 0) cmd = QUIT;
else if(strcmp(msg, "q") == 0 || strcmp(msg, "Q") == 0) cmd = QUIT;
else printf("command not found (? for help)\n");
return cmd;
}
/*******************文件传输的线程********************/
void *transfer_routine(void *arg)
{
//判断发送还是接收
switch(flag)
{
case PUT: //发送文件
put_file(file_name);
break;
case GET: //接收文件
dowload(file_name);
break;
}
//输入提示
write(0,"myftp>", sizeof("myftp>"));
}
/*******************接收并显示服务当前目录的内容********************/
void get_dir()
{
int r = 1; //初始接收一个字节
int get_c = 0; //已经接收的字节数
char r_buf[256]; //接收到完整的文件名缓存
char temp_buf[256]; //接收缓存区
//清空缓存
bzero(r_buf, sizeof(r_buf));
while(1)
{ //清空缓存
bzero(temp_buf, sizeof(temp_buf));
//接收,由于tcp是字节流,因此先接收文件名长度,再接收完整的文件名
r = recv(fd, temp_buf, r, MSG_WAITALL);
//更新已接收的字节数
get_c += r;
//更新已接收的字节数
strcat(r_buf, temp_buf);
//要接收的字节数为0时,则结束
if(r_buf[0] == 0) break;
//判断是否已经完整接收到一个文件名
if((char)get_c != r_buf[0])
{
r = (int)r_buf[0]-get_c;
continue;
}
r_buf[get_c] = '\0';
r_buf[0] = '*';
//打印
printf("%s\n", r_buf);
//初始化
bzero(r_buf, sizeof(r_buf));
get_c = 0;
r = 1;
}
printf("===================================\n");
//输入提示
write(0,"myftp>", sizeof("myftp>"));
}
/******************接收服务器信息线程*******************/
void *rec_msg(void *arg)
{
char r_buf[256]; //接收缓存
int r; //返回值处理
pthread_t tid; //线程id
int *ack = (int *)r_buf;
while(1)
{
//清空缓存
bzero(r_buf, sizeof(r_buf));
//接收
r = recv(fd, r_buf, 255, 0);
if(r == -1)
{
perror("recv fialed");
continue;
}
r_buf[r] = '\0';
//判断工作方式
switch(flag)
{
case GET: //下载文件
if(*ack == READY_T)
{
printf("ready to dowload\n");
pthread_create(&tid, NULL, &transfer_routine, NULL);
}
is_data = 0;
break;
case PUT: //上传文件
if(*ack == READY_T)
{
printf("ready to sent\n");
pthread_create(&tid, NULL, &transfer_routine, NULL);
}
is_data = 0;
break;
case GDIR: //获取目录内容
printf("\n");
get_dir();
break;
case PWD: //当前路径
printf("%s\n", r_buf);
//输入提示
write(0,"myftp>", sizeof("myftp>"));
break;
case CD: //改变当前路径
printf("%s\n", r_buf);
//输入提示
write(0,"myftp>", sizeof("myftp>"));
break;
}
}
}
/***********等待数据TCP连接***************/
void *data_routine(void *arg)
{
socklen_t len;
len = sizeof(data_adr);
while(1)
{
//等待服务器连接
datafd = accept(dfd, (struct sockaddr *)&(data_adr), &len);
is_data = 1;
}
}
/******************列出当前目录的文件及目录*******************/
int ls_dir()
{
DIR *dp = opendir("./");
struct dirent *ep;
int r;
char type[104] = "---";
if(dp == NULL)
{
perror("opendir() failed");
return -1;
}
errno = 0;
while(1)
{
ep = readdir(dp);
if(ep == NULL && errno == 0)
{
break;
}
else if(ep == NULL)
{
perror("readdir() failed");
return -1;
}
if(ep->d_name[0] == '.')
{
continue;
}
bzero(type, sizeof(type));
type[0] = '*';
if(ep->d_type == DT_DIR)
type[1] = 'd';
else
type[1] = '-';
type[2] = '-';
type[3] = '-';
strncat(type, ep->d_name, strlen(ep->d_name)+5);
printf("%s\n", type);
if(r == -1) perror("send err");
}
closedir(dp);
return 0;
}
/****释放资源,退出******/
void quit()
{
shutdown(datafd, SHUT_RDWR);
shutdown(fd, SHUT_RDWR);
close(datafd);
close(fd);
exit(0);
}
/**** 控制台命令输入 ******/
void cmd_console()
{
int r; //处理返回值
char s_buf[256]; //缓存区
char *sent_buf = s_buf;
int local_cmd = 0;
while(1)
{ //清空缓存
bzero(s_buf, sizeof(s_buf));
//输入提示
write(0,"myftp>", sizeof("myftp>"));
//从终端输入命令
gets(s_buf);
if(s_buf[0] == '\0') continue;
//处理输入的命令,判断命令类型
switch(get_cmd(s_buf))
{
case GET: //下载文件
if(strlen(s_buf) < 5)
{
printf("pls input get \n");
break;
}
//跳过get三个字符
sent_buf += 3;
//设置传输文件名
strcpy(file_name, sent_buf+1);
printf("%s\n",file_name);
//设置工作模式为下载文件
flag = GET;
//设置第一个字符为命令编号
sent_buf[0] = (char) flag;
break;
case PUT: //上传文件
if(strlen(s_buf) < 5)
{
printf("pls input put \n");
break;
}
//跳过put三个字符
sent_buf += 3;
//设置传输文件名
strcpy(file_name, sent_buf+1);
//设置工作模式为上传文件
flag = PUT;
sent_buf[0] = (char) flag;
break;
case GDIR: //获取目录内容
flag = GDIR;
printf("=========服务器当前目录文件=========\n");
sent_buf[0] = (char) flag;
sent_buf[1] = '\0';
break;
case PWD: //获取当前路径
flag = PWD;
//发送命令给服务器
sent_buf[0] = (char) flag;
sent_buf[1] = '\0';
//r = send(fd, s_buf, strlen(s_buf), 0);
break;
case CD: //改变当前路径
flag = CD;
sent_buf += 2;
sent_buf[0] = (char) flag;
break;
case HELP: //显示命令帮助信息
show_help();
local_cmd = 1;
break;
case LS:
printf("=========本地当前目录文件=========\n");
ls_dir();
printf("=================================\n");
local_cmd = 1;
break;
case QUIT: //退出
quit();
break;
default:
break;
}
if(!local_cmd)
{
//发送命令给服务器
r = send(fd, sent_buf, strlen(sent_buf), 0);
//指回缓存区的开头
sent_buf = s_buf;
}
local_cmd = 0;
}
}
/******************主函数*******************/
int main(int argc, char const *argv[])
{
/***默认服务器IP地址*****/
char sip[20] = "192.168.1.210";
int r; //处理返回值
pthread_t tid; //线程id
int ack;
int data_port = 31629; //数据传输端口
if(argc < 2)
{
printf("pls input: %s [ip]\n", argv[0]);
exit(0);
}
flag = -1; //工作模式初始化为无
is_data = 0;
port = 2121; //默认端口为2121
if(argc > 2)
{
//自己本地定义端口
int n = strlen(argv[2]);
int i;
data_port = 0;
for( i=0; i |