




已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
FTP 协议实验报告 实验目的 1、在 Linux 系统上完成一个文件传输协议(FTP )的简单实现。 2、深入理解 FTP 协议的原理和协议细节。 3、学会利用 Socket 接口设计实现简单应用层协议。 4、掌握 TCP/IP 网络应用程序的基本设计方法和实现技巧。 实验原理 1、 FTP 协议 FTP 是 File Transfer Protocol,即文件传输协议的缩写。该协议用于在两台计算机之间 传送文件。FTP 会话包括了两个通道,一个是控制通道,一个是数据通道。控制通道是和 FTP 服务器进行沟通的通道,连接 FTP 服务器,发送 FTP 指令;数据通道则是和 FTP 服务 器进行文件传输或者获取文件列表的通道。 FTP 协议中,控制连接的各种指令均由客户端主动发起,而数据连接有两种工作方式: 主动方式 (PORT 方式)和被动方式(PASV 方式) 。主动方式下,FTP 客户端首先和 FTP 服 务器的控制通道对应端口(一般为 21)建立连接,通过控制通道发送命令,客户端需要接 收数据的时候在这个通道上发送 PORT 命令。PORT 命令包含了客户端用什么端口(一个大 于 1024 的端口)接收数据。在传输数据的时候,FTP 服务器必须和客户端建立一个新的连 接,服务器通过自己的 TCP 20 端口发送数据。被动方式下,建立控制通道的过程和主动方 式类似,当客户端通过这个通道 发送 PASV 命令的时候,FTP server 打开一个位于 1024- 5000 之间的随机端口并且通知客户端,然后客户端与服务器之间将通过这个端口进行数据 的传送。 2、 socket 编程 (1 )什么是 Socket Socket 接口是 TCP/IP 网络的 API,Socket 接口定义了许多函数或例程。网络的 Socket 数据传输是一种特殊的 I/O,Socket 也是一种文件描述符。 (2 ) Socket 的建立 为了建立 Socket,程序可以调用 Socket 函数,该函数返回一个 socket 描述符。Socket 描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用 Socket 函数时, socket 执行体将建立一个 Socket,实际上“建立一个 Socket“意味着为一个 Socket 数据结构 分配存储空间。Socket 执行体为你管理描述符表。两个网络程序之间的一个网络连接包括 五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。 Socket 数据结构中包含这五种信息。 socket 函数原型为:int socket(int domain, int type, int protocol); domain:指明所使用的协议族,通常为 PF_INET,表示互联网协议族(TCP/IP 协议族) ; type:指定 socket 的类型为 SOCK_STREAM 或 SOCK_DGRAM,Socket 接口还定义了原始 Socket(SOCK_RAW) ,允许程序使用低层协议; protocol:通常赋值“0“。 返回:整型 socket 描述符。 (3 ) Socket 配置 无连接 socket 的客户端和服务端以及面向连接 socket 的服务端通过调用 bind 函数来 配置本地信息。 Bind 函数将 socket 与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。 Bind 函数原型为: int bind(int sockfd,struct sockaddr_in *my_addr, int addrlen); Sockfd:调用 socket 函数返回的 socket 描述符 my_addr:一个指向包含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针 addrlen:常被设置为 sizeof(struct sockaddr)。 struct sockaddr_in 结构类型是用来保存 socket 信息的: struct sockaddr_in short int sin_family; /* 地址族 */ unsigned short int sin_port; /* 端口号 */ struct in_addr sin_addr; /* IP 地址 */ unsigned char sin_zero8; /* 填充 0 以保持与 struct sockaddr 同样大小 */ ; sin_zero:用来将 sockaddr_in 结构填充到与 struct sockaddr 同样的长度,可以用 bzero()或 memset()函数将其置为零。 使用 bind 函数时,可以用下面的赋值实现自动获得本机 IP 地址和随机获取一个没有 被占用的端口号: my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机 IP 地址 */ 通过将 my_addr.sin_port 置为 0,函数会自动为你选择一个未占用的端口来使用。同样, 通过将 my_addr.sin_addr.s_addr 置为 INADDR_ANY,系统会自动填入本机 IP 地址。 Bind()函数在成功被调用时返回 0;出现错误时返回“-1“并将 errno 置为相应的错误号。 需要注意的是,在调用 bind 函数时一般不要将端口号置为小于 1024 的值,因为 1 到 1024 是保留端口号,你可以选择大于 1024 中的任何一个没有被占用的端口号。 (4 )连接建立 无连接协议从不建立直接连接。面向连接的服务器也从不启动一个连接,它只是被动 的在协议端口监听客户的请求。 Listen 函数使 socket 处于被动的监听模式,并为该 socket 建立一个输入数据队列,将 到达的服务请求保存在此队列中,直到程序处理它们。 int listen(int sockfd, int backlog); Sockfd: Socket 系统调用返回的 socket 描述符 backlog:指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待 accept()。 如果一个服务请求到来时,输入队列已满,该 socket 将拒绝连接请求,客户将收到一个出 错信息。 返回:当出现错误时 listen 函数返回 -1,并置相应的 errno 错误码。 accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用 accept 函数,然后睡眠并等待客户的连接请求。 int accept(int sockfd, void *addr, int *addrlen); sockfd:被监听的 socket 描述符; addr:通常是一个指向 sockaddr_in 变量的指针,该变量用来存放提出连接请求服务的主机 的信息(某台主机从某个端口发出该请求) ; addrten:通常为一个指向值为 sizeof(struct sockaddr_in)的整型指针变量; 返回:出现错误时 accept 函数返回 -1 并置相应的 errno 值。 3 (5 )结束传输 当所有的数据操作结束以后,你可以调用 close()函数来释放该 socket,从而停止在该 socket 上的任何数据操作:close(sockfd); 实验内容 在 Linux 系统上使用 Socket 接口实现 FTP 客户端 和服务器的程序,使客户端可以连接 至服务器,并且可以进行一些 FTP 的基本操作,如列出目录、下载文件等。从 FTP 协议的 实现角度来看,客户端 与服务器的命令通道和数据通道需要分享,同时应该支持以下一些 FTP 命令: get:取远方的一个文件。 put:传给远方一个文件。 pwd:显示远方当前目录。 dir:列出远方当前目录。 cd:改变远方当前目录。 ?:显示你提供的命令 quit:退出返回 实验过程 1、实现服务器端 (1 )全局变量 为了记录缓冲区大小、当前目录、当前工作路径、帮助信息而定义了以下几个全局变量: #define dataLen 1024 /缓冲区大小 char currentDirPath200; /当前工作目录的绝对路径 char currentDirName30; /当前目录的名称 char help=“get get a file from servern put upload a file to servern pwd display the current directory of servern dir display the files in the current directory of servern cd change the directory of servern ? display the whole command which equals helpn quit returnn“; /帮助信息 (2 )函数 在服务器端实现了以下几个函数: char *getDirName(char *dirPathName); /根据当前工作目录的绝对路径得到当前目录名 称 void cmd_pwd(int sock); /处理 pwd 命令 void cmd_dir(int sock); /处理 dir 命令 void cmd_cd(int sock,char *dirName); /处理 cd 命令 void cmd_help(int sock); /处理?命令 void cmd_get(int sock,char*fileName); /处理 get 命令 void cmd_put(int sock,char *fileName); /处理 put 命令 (3 )主函数的实现: 先建立数据通道和命令通道,然后监听,若有客户端连上,则建立一个子进程,先向 客户端发送帮助信息,然后根据客户端的命令来调用上述各函数来处理。 int main(int argc,char *argv) int sock; /服务器用于监听的数据通道 int sockmsg; /服务器用于监听的命令通道 char client_cmd10; /客户端出发的命令 char cmd_arg20; /客户端输入的文件或目录名,用在cd,put,get命令 中 struct sockaddr_in server; /服务器数据通道的信息 struct sockaddr_in servermsg; /服务器命令通道的信息 int datasock; /用于通信的数据通道 int msgsock; /用于通信的命令通道 pid_t child; /client子进程 sock=socket(AF_INET,SOCK_STREAM,0); /创建用于传数据的套接字 sockmsg=socket(AF_INET,SOCK_STREAM,0); /创建用于传消息的套接字 int opt = 1,opt2 = 1; setsockopt(sock , SOL_SOCKET , SO_REUSEADDR , /实现sock的重用 setsockopt(sockmsg , SOL_SOCKET , SO_REUSEADDR , /实现sockmsg的重 用 if (sock = 0;i -) /找到最后一个/号,之后的字符串即为当前工作目录名称 if(dirPathNamei = /) pos=i; break; dirName = (char *)malloc(len - pos + 1); for(i = pos + 1;i d_name; memset(filePath,0,sizeof(filePath); strcpy(filePath,currentDirPath); 9 strcat(filePath,“/“); strcat(filePath, fileName); fd = open(filePath, O_RDONLY, S_IREAD); fstat(fd, if(S_ISDIR(fileSta.st_mode)/检查是否为目录 strcat(fileInfo,“dirt“); strcat(fileInfo,fileName); else strcat(fileInfo,“filet“); strcat(fileInfo,fileName); write(sock,fileInfo,sizeof(fileInfo); closedir(pdir); (7) void cmd_cd(int sock,char *dirName)的实现: 遍历当前目录查看是否有与客户输入的目录名相同的目录,这里要区分普通目录和上 一级目录(即),若为普通目录则直接在存放当前目录绝对地址的字符串的后面加上新 进的目录,若为上一级目录,则要在存放当前目录绝对地址的字符串中删去最后一个目录, 即最后一个“/”号之后的内容。 void cmd_cd(int sock,char *dirName) DIR *pdir; struct dirent *pent; char filename30; int i,fcounts=0; int flag=0; pdir=opendir(currentDirPath); pent=readdir(pdir); while(pent!=NULL) fcounts+; pent=readdir(pdir); closedir(pdir); if(fcountsd_name,dirName)=0) if(strcmp(dirName,“) = 0) int i,pos = 0; int len=strlen(currentDirPath); for(i = len - 1;i = 0;i -) if(currentDirPathi = /) pos=i; break; currentDirPathpos=0; else strcat(currentDirPath,“/“); strcat(currentDirPath,dirName); char *savePointer = getDirName(currentDirPath); strcpy(currentDirName,savePointer); flag=1; break; if(flag=1) write(sock,currentDirPath,sizeof(currentDirPath); closedir(pdir); (8)void cmd_help(int sock)的实现:把存有帮助信息的字符串发到客户端 void cmd_help(int sock) int len=strlen(help)+1; write(sock,help,len); (9)void cmd_get(int sock,char*fileName)的实现: 把文件内容一部分一部分地读到缓冲区,然后发给客户端 void cmd_get(int sock,char*fileName) int fd; struct stat fileSta; long fileSize; char filePath200, bufdataLen; 11 memset(filePath,0,sizeof(filePath); strcpy(filePath,currentDirPath); strcat(filePath,“/“); strcat(filePath,fileName); fd=open(filePath,O_RDONLY, S_IREAD); if(fd! = -1) fstat(fd, fileSize=(long) fileSta.st_size; write(sock, memset(buf,0,dataLen); while(fileSize dataLen) read(fd,buf,dataLen); write(sock,buf,dataLen); fileSize=fileSize-dataLen; read(fd,buf,fileSize); write(sock,buf,fileSize); close(fd); printf(“transfer completedn“); else printf(“open file %s failedn“,filePath); (10)void cmd_put(int sock,char *fileName)的实现: 从客户端一部分一部分地读文件到续冲区,然后写入文件。 void cmd_put(int sock,char *fileName) int fd; long fileSize; char filePath200, bufdataLen; strcpy(filePath,currentDirPath); strcat(filePath,“/“); strcat(filePath,fileName); fd=open(filePath,O_RDWR|O_CREAT, S_IREAD|S_IWRITE); if(fd!=-1) memset(buf,0,dataLen); read(sock, while(fileSizedataLen) read(sock,buf,dataLen); write(fd,buf,dataLen); fileSize=fileSize-dataLen; read(sock,buf,fileSize); write(fd,buf,fileSize); close(fd); printf(“transfer completedn“); else printf(“open file %s failedn“,filePath); 2、客户端的实现 (1)全局变量 #define dataLen 1024 /缓冲区大小 char user_cmd10; /客户端出发的命令 char cmd_arg20; /客户端输入的文件或目录名 char bufdataLen; /缓冲区 (2)函数 void cmd_pwd(int sock,int sockmsg); /处理pwd命令 void cmd_dir(int sock,int sockmsg); /处理dir命令 void cmd_cd(int sock,int sockmsg,char *dirName); /处理cd命令 void cmd_help(int sock,int sockmsg); /处理?命令 void cmd_get(int sock,int sockmsg,char *fileName); /处理get命令 void cmd_put(int sock,int sockmsg,char *fileName); /处理put命令 void cmd_quit(int sock,int sockmsg); /处理put命令 (3)主函数的实现: 先与服务器端建立连接,然后处理向服务客端发送的各种命令。 int main(int argc,char *argv) int cmd_len,arg_len; int sock, sockmsg; struct sockaddr_in server, servermsg; struct hostent *hp; sock = socket(AF_INET,SOCK_STREAM,0); sockmsg = socket(AF_INET,SOCK_STREAM,0); if (sock n“); return 0; server.sin_family=AF_INET; server.sin_port=htons(atoi(argv2); inet_pton(AF_INET,argv1, 13 servermsg.sin_family=AF_INET; servermsg.sin_port=htons(atoi(argv2) + 1); inet_pton(AF_INET,argv1, if (connect(sock,(struct sockaddr *)idataLen) read(sock,buf,dataLen); write(fd,buf,dataLen); fileSize=fileSize-dataLen; read(sock,buf,fileSize); write(fd,buf,fileSize); close(fd); printf(“download completedn“); else printf(“open file %s failedn“,localFilePath); (9)void cmd_put(int sock,int sockmsg,char *fileName)的实现: 与cmd_get命令类似,先向服务器发出put命令和文件名,然后把文件一部分一部分地 读到缓冲区,然后再发送到服务器。 void cmd_put(int sock,int sockmsg,char* fileName) write(sockmsg,user_cmd,sizeof(user_cmd); write(sockmsg,cmd_arg,sizeof(cmd_arg); int fd; long fileSize; int numRead; char filePath200; struct
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 博图SCL官方培训
- 软包电池外观培训
- 城市交通规划合同管理论文咨询重点基础知识点
- 产品组装技能培训课件
- 【培训课件】非处方药市场推广策略
- 配方出售保密协议书模板
- 《医疗设备监测的护理》课件
- 车辆无偿借用合同协议
- 公司培训计划书
- 连锁餐厅转让合同协议
- 丝路神话-“一带一路”沿线古今漫谈知到章节答案智慧树2023年黑龙江林业职业技术学院
- 乡村规划与设计教材课件
- 2023年高考-汉语文试卷及答案
- 【小区植物配置情况调研分析8500字(论文)】
- 航空油料特种设备修理员-航空油料特种设备修理员精选试题
- 2023年彭泽县小升初英语考试题库及答案解析
- 休闲农业与乡村旅游(课件)
- JB/T 20074-2020药用配液罐
- LOI意向书中英文模板
- GB/T 4458.1-2002机械制图图样画法视图
- GB/T 38192-2019注射成型塑料圆柱齿轮精度制轮齿同侧齿面偏差和径向综合偏差的定义和允许值
评论
0/150
提交评论