重庆大学课程设计报告
课程设计题目:Linux下基于socket的文件传输程序设计
学 院: 计算机学院
专业班级: 网络工程一班
年 级:
姓 名:
学 号:
完成时间: 2012 年 06 月 22 日
成 绩:
指导教师:
重庆大学教务处制
课程设计指导教师评定成绩表
指导教师评定成绩:
指导教师签名: 年 月 日
重庆大学本科学生课程设计任务书
计算机Linux课程设计报告书
——文件传输系统
目录
Ⅰ 引言 …………………………………………………………………………………2
Ⅱ 总体设计 ……………………………………………………………………………2
2.1总体设计概念 ……………………………………………………………………2
2.2系统架构 …………………………………………………………………………2
2.3 模块划分 …………………………………………………………………………2
Ⅲ 详细设计 ……………………………………………………………………………3
3.1软件层次模型 ……………………………………………………………………3
3.2流程图 ……………………………………………………………………………4
Ⅳ 编程及运行结果 ……………………………………………………………………5
4.1服务器端设计与编码 ……………………………………………………………5
4.2客户端设计与编码 ………………………………………………………………12
Ⅴ 课程设计总结与体会…………………………………………………………………23
Ⅵ 参考文献……………………………………………………………………………24
Ⅰ 引言
随着我们的生活与计算机越来越紧密的结合起来,计算机可以取代生活中很多的事情。最简单的,计算机的文件传输就可以避免远距离纸质文件传输带来的不便。在计算机网络中,文件传输只是一个很小很细微的功能。但它到底是怎样实现的呢?下面就让我们自己完成一个简单的实现文件传输功能的传输器吧。
Ⅱ 总体设计
2.1 总体设计概念
为了实现文件传输等功能,服务器与客户端采用TCP/IP连接方式。程序启动时,服务器开始监听,客户端连接服务器。在客户端和服务器都各自实现相应功能。当实现相应功能时,应注意权限设置。
2.2 系统框架
选择传输控制协议TCP,分别建立客户端与服务器端;并设置相应的框架。
2.3 模块划分
1)服务器端,主要实现向各个客户端发送消息,接受来自客户端的各种信息并综合处理,具体功能如下:
①连接控制: 包括启动服务器、断开服务器以及断开某个客户端的连接;
②管理作用: 包括对参与聊天者的昵称进行修改以及向所有或某个客户端发送消息,可以创建,删除,重命名文件等。
③登陆信息: 检查用户是否已登陆,如登录,显示登录用户。
④消息处理: 解析客户端与服务器端的消息交互类型,并做出相应处理,能够找到消息所对应的接收端。
⑤监听作用: 实现对服务器发送过来的消息进行监听的功能;
2)客户端:主要实现向服务器端发布消息,并且对来自服务器的消息做出相应的响应。具体功能如下:
①连接功能: 输入服务器地址,实现登录及断开功能
②登录设置: 登录时,发送消息使服务器端获得相关登录信息。
③消息处理: 接收由服务器端发送来的信息,并做出相应的响应;
④消息处理: 相应从服务器端接收到的消息交互类型,实现用户与用户之间的信息交互和文件交互;
⑤文件上传,下载:实现文件上传,下载以及查看服务器下默认的文件列表等。
Ⅲ 详细设计
3.1 软件层次模型
服务器层次结构:
客户端层次结构:
3.2 流程图
根据以上的程序模块划分,设计好服务器端和客户端的流程图,如下:
服务器端流程:
客户端流程:
Ⅳ 编程以及运行结果
4.1 服务器
1.进入服务器,等待连接。
int main(int argc, char **argv)
{
printf("\n欢迎来到张驹强,李欢和谭建的FTP服务器\n");
//get the server socket
server_sock = socket(PF_INET, SOCK_STREAM, 0);
if(server_sock == -1) //create socket failed
{
printf("请求socket创建失败\n");
exit(-1);
}
else printf("请求socket创建成功!\n");
//configure server address,port
memset(&server_addr, 0 ,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(server_addr.sin_addr.s_addr == INADDR_NONE)
{
printf("IP地址错误!\n");
exit(-1);
}
server_adr_len = sizeof server_addr;
//bind
z = bind(server_sock, (struct sockaddr *)&server_addr, server_adr_len);
if(z == -1)
{
printf("绑定失败!\n");
exit(-1);
}
else printf("绑定成功!\n");
//listen
z = listen(server_sock, 5);
if(z < 0)
{
printf("服务器无法监听!");
exit(1);
}
else printf("服务器开始监听\n");
//loop and wait for connection
while(1)
{
//struct sockaddr_in client_addr;
client_addr_len = sizeof(client_addr);
i = 0;
printf("等待连接请求...\n");
//accept an request
while(1) {
pthread_t thread;
client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_sock < 0 ) {
printf( " 请求出错!\n " );
continue;
}
//实现服务器多线程操作
if((pthread_create(&thread,NULL,handleFunc,&client_sock) )!= 0) {
printf("创建线程失败!\n");
break;
}
}
}
close(server_sock);
return 0;
}
2.建立连接。
void command_port(char *params, char *reply)
{
unsigned long a0, a1, a2, a3, p0, p1, addr;
sscanf(params, "%lu,%lu,%lu,%lu,%lu,%lu", &a0, &a1, &a2, &a3, &p0, &p1);
addr = htonl((a0 << 24) + (a1 << 16) + (a2 << 8) + a3);
if(addr != client_addr.sin_addr.s_addr)
{
stpcpy(reply, "数据传输socket请求IP地址错误!\n");
return;
}
//setup the port for the data connection
memset(&data_addr, 0, sizeof(data_addr));
data_addr.sin_family = AF_INET;
data_addr.sin_addr.s_addr = addr;
data_addr.sin_port = htons((p0 << 8) + p1);
//get the data cocket
data_sock = socket(PF_INET, SOCK_STREAM, 0);
if(data_sock == -1)
{
printf("数据传输socket创建失败!");
exit(-1);
stpcpy(reply, "无法创建数据传输socket\r\n");
return;
}
//connect to the client data socket
if(connect(data_sock, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0)
{
printf("数据传输socket连接失败!\r\n");
exit(-1);
stpcpy(reply, "数据传输socket连接失败!\r\n");
return;
}
stpcpy(reply, "双方连接建立成功!\r\n");
}
3.显示登录信息。
if(strcmp(command, "USE") == 0)//USER
{
stpcpy(reply, "suc\r\n");
write(clifd, reply, strlen(reply));
printf("%s进入服务器 \n", buffer+4);
continue;
}
4.创建文件。
if(stat(filename, &sbuf) == -1)
{
//printf("创建文件失败.\n");
}
outfile = fopen(filename, "w");
if(outfile == 0)
{
printf("创建文件失败 \r\n");
exit(-1);
stpcpy(reply, "创建文件失败 \r\n");
close(data_sock);
return;
}
5.删除文件。
void command_delete(char filename[],char *reply){
int i=remove(filename);
if(i<0)
stpcpy(reply, "删除失败,可能由于目标文件不存在\r\n");
else
stpcpy(reply, "删除成功\r\n");
}
6.重命名文件。
void command_rename(char filename[],char newname[],char *reply){
//printf("%s\n", filename);
//printf("%s\n", newname);
int i=rename(filename,newname);
if(i<0)
stpcpy(reply, "重命名失败,可能由于目标文件不存在\r\n");
else
stpcpy(reply, "重命名成功\r\n");
}
7.显示当前目录。
void command_list(char *reply)
{
int n=0;
char info[MAX_INPUT_SIZE];
char buf[MAX_INPUT_SIZE];
stpcpy(buf, "");
DIR *mydir;
struct dirent *myitem;
mydir = opendir(".");
while ((myitem = readdir(mydir)) != NULL)
{
if ((strcmp(myitem->d_name, ".") == 0) || (strcmp(myitem->d_name, "..") == 0))
continue;
stpcpy(info, myitem->d_name);
strcat(info,"\t");
strcat(buf,info);
n++;
if(n==4){
strcat(buf,"\n");
n=0;
}
}
closedir(mydir);
stpcpy(reply, buf);
return;
}
上图为服务器端运行结果。
4.2 客户端
1.建立连接。
server_ip = LOCAL_IP;
server_port = SERVER_PORT;
//connect to the ftp server
server_host = gethostbyname(server_ip);
if(server_host == (struct hostent *)NULL)
{
printf(">gethostbyname failed\n");
exit(1);
}
//setup the port for the connection
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, server_host->h_addr, server_host->h_length);
server_addr.sin_port = htons(server_port);
//get the socket
server_sock = socket(PF_INET, SOCK_STREAM, 0);
if(server_sock < 0)
{
printf(">error on socket()\n");
exit(1);
}
//connect to the server
if(connect(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
printf(">error on connect()\n");
close(server_sock);
exit(1);
}
//connect to the ftp server successful
printf(">连接到 %s:%d\n", server_ip, server_port);
user_login();
help_info();
2.连接到服务器。
void user_login()
{
printf(">用户名称:");
fgets(line_in, MAX_INPUT_SIZE, stdin);
line_in[strlen(line_in)-1] = '\0';
stpcpy(name, line_in);
send_msg("USER", line_in, 1);
z = read(server_sock, buffer, sizeof(buffer));
//printf("%s\n",buffer);
buffer[z-2] = 0;
if(strncmp("suc", buffer, 3) == 0)
{
printf("欢迎来到FTP:%s\n",name);
printf("请选择操作\n");
}
else{
printf("被服务器拒绝\n");
}
return;
}
3.帮助界面。
void help_info()
{
printf("? or help\t获得帮助信息\n");
printf("ls\t获得当前目录下的文件\n");
printf("pwd\t获得当前路径\n");
printf("cd\t修改路径(需要高级权限)\n");
printf("get <文件名>\t下载数据\n");
printf("put <文件名>\t上传数据\n");
printf("apply <密码>\t申请高级权限\n");
printf("rename <文件名>\t修改服务器中的中文名(需要高级权限)\n");
printf("rm\t删除服务器中的文件(需要高级权限)\n");
printf("quit\t退出FTP\n");
}
4.获得服务器当前路径。
void command_pwd()
{
send_msg("PWD", name, 0);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
}
5.修改路径。
void command_cd()
{
send_msg("CWD", &line_in[3], 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
}
6.传输文件。
int command_port()
{
if(client_sock < 0)
{
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof local_addr;
memset(&local_addr, 0, sizeof local_addr);
if(getsockname(server_sock, (struct sockaddr *)&local_addr, &local_addr_len) != 0)
{
printf("获得本地ip地址失败\n");
return -1;
}
local_ip = inet_ntoa(local_addr.sin_addr);
local_port = local_addr.sin_port;
//printf("local addr %s:%d\n", local_ip, local_port);
char client_port[8] = "";
sprintf(client_port, "%d.%d", (int)(local_port/256), (int)(1 + local_port - 256 * (int)(local_port/256)));
sprintf(port_addr, "%s.%s", local_ip, client_port);
replace(port_addr, ".", ",", 24);
if(data_conn(local_ip, local_port + 1) == -1) return -1;
}
send_msg("PORT", port_addr, 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
//printf("%s\n", buffer);
//accept an request
socklen_t data_addr_len = sizeof data_addr;
memset(&data_addr, 0, sizeof data_addr);
data_sock = accept(client_sock, (struct sockaddr *)&data_addr, &data_addr_len);
if(data_sock < 0)
{
printf("传输数据socket创建失败!\n");
return -1;
}
return 0;
}
int data_conn(char *ip, int port)
{
client_sock = socket(PF_INET, SOCK_STREAM, 0);
if(client_sock == -1) //create socket failed
{
close(client_sock);
printf("传输数据socket创建失败!\n");
return -1;
}
//configure server address,port
memset(&client_addr, 0 ,sizeof(client_addr));
socklen_t client_addr_len = sizeof client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(port);
//client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
client_addr.sin_addr.s_addr = inet_addr(ip);
if(client_addr.sin_addr.s_addr == INADDR_NONE)
{
printf("IP地址错误\n");
return -1;
}
//bind
z = bind(client_sock, (struct sockaddr *)&client_addr, client_addr_len);
if(z == -1)
{
close(client_sock);
perror("传输数据socket绑定失败\n");
return -1;
}
//listen
z = listen(client_sock, 1);
if(z < 0)
{
close(client_sock);
printf("传输数据socket监听失败!\n");
return -1;
}
printf("传输数据socket正在监听...\n");
return 0;
}
7.申请权限。
void command_apply(char *code){
send_msg("APPL", code, 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
if(strncmp(buffer, "Y", 1)==0)
{
p=1;
send_msg("APSU", name, 1);
printf("权限申请成功\n");
}else{
p=0;
printf("权限申请失败\n");
}
}
8.删除服务器下的文件。
void command_delete(char *filename){
if(strlen(filename)==0)
{
printf("文件名不能为空\n");
return;
}
send_msg("DELE", filename, 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
}
9.重命名服务器文件名。
void command_rename(char *filename,char *newname){
send_msg("RENA", filename, 1);
send_msg("RENA", newname, 1);
//printf("%s\n", filename);
//printf("%s\n", newname);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
}
10.下载数据。
void command_get(char *filename)
{
FILE *outfile;
short file_open=0;
unsigned char databuf[FILEBUF_SIZE];
int bytes = 0, bytesread = 0;
if(strlen(filename)==0)
{
printf("文件名不能为空\n");
return;
}
if(command_port() == -1) return;
send_msg("RETR", filename, 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
/* Read from the socket and write to the file */
while ((bytes = read(data_sock, databuf, FILEBUF_SIZE)) > 0) {
if (file_open == 0) {
/* Open the file the first time we actually read data */
if ((outfile = fopen(filename, "w")) == 0) {
printf("打开文件失败\n");
close(data_sock);
return;
}
file_open = 1;
}
write(fileno(outfile), databuf, bytes);
bytesread += bytes;
}
/* Close the file and socket */
if (file_open != 0) fclose(outfile);
close(data_sock);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
printf("数据量:%d.\n", bytesread);
}
11.上传数据。
void command_put(char *filename)
{
FILE *infile;
unsigned char databuf[FILEBUF_SIZE] = "";
int bytes, bytessend;
if(strlen(filename)==0)
{
printf("文件名不能为空\n");
return;
}
infile = fopen(filename,"r");
if(infile == 0)
{
printf("打开文件失败\n");
return;
}
if(command_port() == -1) return;
send_msg("STOR", filename, 1);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
while((bytes = read(fileno(infile), databuf, FILEBUF_SIZE)) > 0)
{
write(data_sock, (const char *)databuf, bytes);
bytessend += bytes;
memset(&databuf, 0, FILEBUF_SIZE);
}
memset(&databuf, 0, FILEBUF_SIZE);
fclose(infile);
close(data_sock);
z = read(server_sock, buffer, sizeof(buffer));
buffer[z-2] = 0;
printf("%s\n", buffer);
printf("数据量%d \n", bytessend);
return;
}
12.退出。
void command_quit()
{
send_msg("QUIT",name, 1);
printf("再见,%s\n", name);
}
上图为客户端各种操作运行截图。
Ⅴ 课程设计总结与体会
在本次课程设计中,我们综合应用所学过的知识,在Socket编程机制的基础上,根据TCP协议的原理,实现服务器端和客户端的主要功能。主要功能的操作全部可以在客户端上操作,服务器端主要实现向各个客户端发送消息,接受来自客户端的各种信息并综合处理。
这次课程设计是对我们一学期Linux课程的检测,让我们更好的将学到的Linux编程方法运用到具体的功能程序中。本次课程设计中,我们设计小组对理论知识进行了充分研究,发扬了创新实践精神,积极探索,努力勤勉,共同越过了设计路上的每一处障碍,攻克了一个又一个的难关。最终,友好的协作关系与团队凝聚力促使我们顺利地完成了此次课程设计。
本次课程设计,加深了我们对理论知识的理解,也锻炼了我们的实践能力,更多的是在实践中收获了太多的感触和心得,.虽然计算机组成原理的课程设计已经结束,可我们明白“学无止境”的道理,我们会继续刻苦钻研,求实创新,不断地用知识来充实自己,跟上科技时代前进的步伐。
在此次的设计中,非常感谢老师对我们的帮助和指导。才疏学浅,过程难免还不够完善,望老师再次不吝赐教!
Ⅵ 参考文献
[1]美Robert Love著_陈莉君 康华译_Linux 内核设计与实现_机械工业出版社_2011.6
[2]美 DanielP. Bovet著_陈莉群 冯锐 牛欣源 译_深入理解LINUX内核_中国电力出版社_2008