1、FTP协议的应用第九组:FTP协议的应用李伟深(电信113)学号1107400080邓景杭(电信112)学号1107400058孟永健(电信112)学号1107400043孔志荣(电信112)学号1107400052苏健 (电信113)学号1107400054叶云青(电信113)学号1107400057叶杨杰 (电信113学号1107400065张铭魁(电信113)学号1107400067甘启钊(电信113)学号1107400062钟泽豪(电信113)学号1107400060FTP服务器端设计说明书1、概述FTP作为文件传输协议(File Transfer Protocol),其任务务是从一台
2、计算机将文件传送到另一台计算机,它与这两台计算机所处的位置、联系的方式、以及使用的操作系统无关。FTP采用“客户机/服务器”方式,客户端要在自己的本地计算机上安装FTP客户程序,而服务器端则提供远程的访问和文件传输。针对FTP的服务器端来说, 主要为用户和管理员提供访问权限,并通过侦听端口以随时响应合法的客户请求。通过控制通道和数据通道分别控制和响应对应的相关请求以及进行所需的数据文件传输。 响应的基本指令同标准的FTP协议规范以做到兼容现行的FTP客户端软件(如Flashxp等),同时主要要保证同本组对应的FTP客户端程序相兼容,确保其正确性和可扩展性。基于服务器端的功能和特点,需要保证能够
3、对端口进行实时侦听,及时响应客户端发送的命令,并由此打开控制通道,等待客户端用户的命令再做对应处理,开放数据通道进行信息和文件等数据的传输。服务器需要能够对用户进行认证并对命令做出及时准确的回应,满足传输等需求。由此在设计实现服务器端时,要能够建立一定的客户访问机制,设置权限,针对不同的用户确定相应的处理机制以实现“合法”的访问传输,更重要的一点是正确地解析来自客户端发出的请求命令,给予实时同时需准确无误地响应。要在客户端完成登陆,退出,列目录,下载/上传文件,下载/上传目录,删除文件/目录,新建目录,断点续传等操作的基础上针对每个操作的对应命令做出回复完成合法用户所需的功能。2、开发目标设计
4、并实现一个符合标准FTP协议规范的,并支持拥有登陆,退出,列目录,下载/上传文件,下载/上传目录,删除文件/目录,新建目录,断点续传以及传输过程中状态计算等功能的对应FTP客户端系统和兼容有同样功能的现行FTP客户端软件。二、参考文档协议说明书Visual C+ 6.0网络及Internet开发指南Visual C+网络程序设计实例详解Visual C+网络高级编程三、开发工具和平台基于Visual C+ 6.0的SOCKET网络编程,在Windows XP SP3下开发完成。由于近阶段一直在使用VC6.0完成相关实习,并需要同客户端兼容,就决定采用VC6.0下的MFC支持SOCKET进行FT
5、P的开发。四、测试工具由于并行开发和需要兼容现行FTP的缘故,为方便测试,采用Flashxp作为客户端软件调试,当然后期主要是直接同本组客户端进行连接测试。5、程序结构 1)类定义classspecificationCAlienFTPServerDlg主对话框类,用于图形界面的处理,通过调用AlienFTPServerCmd类提供的函数实现支持客户端的请求并做出响应。 AlienFTPServerCmdFTP命令处理类,该类主要负责实现功能需求的各种函数,供CAlienFTPServerDlg直接调用,同时也提供一些基本操作使CAlienFTPServerDlg可以通过这些操作的组合完成复杂的
6、功能。几乎所有的响应指令都是通过该类完成。CAlienFTPServer对应的工程类,基本没有完成相关操作 Adduser用于增加用户的对话框类,实际中应由管理员进行分配,使用户在持有用户名和密码的情况下访问FTP服务器,同时也支持匿名登陆。 2)主要类接口2.1 CAlienFTPServerDlg类classmemberfunctionOnStart用于侦听客户端的请求以开启服务器的相关服务,它调用ThreadStart的线程处理该操作。OnViewpath用于浏览选择对应主机上的文件夹路径供用户访问,它同服务器端和客户端其它处理相同类型的操作所采用的方式基本相同。OnAddUser该功能
7、比较简单,主要是跳出增加用户的对话框以设置相关信息,主要功能实现还是在AlienFTPServerCmd中。2.2 AlienFTPServerCmd类该类完成的操作较为复杂,几乎所有服务器的处理工作都由它完成,故只针对部分进行介绍。classmemberfunctionAddUser增加访问服务器的用户操作,并将信息记录在Usernode的结构体中。GetUserListHead得到用户列表中的第一个用户信息(主要是用户名),其它如GetUserListLast等函数功能与之类似。GetUserLogin得到当前登陆的用户信息(用户名),其它如GetUserPasswd其功能和结构与之相似G
8、etUserStartDir得到对应的用户登陆的根目录信息StartListening开启端口进行侦听,相应的StopListening则为结束侦听IsListening确定当前服务器是否处于Listening状态StartAccepting允许客户端接入服务器进行相应操作SendReply服务器对客户端相应命令作出的响应CloseSocket关闭套接字BuildPath根据客户的起始路径和控制构造相应路径LIST_Command处理客户端List等命令的功能STOR_Command处理客户端Stor等命令的功能OpenDataConnection因数据传输打开数据通道3)线程定义Thread
9、specificationThreadStart用于开启服务器响应服务时为其开辟的线程以防止服务器在忙于侦听和处理客户端请求时,无法响应其他的界面处理等操作。5、功能模块设计1)总体流程 服务器会先与客户端进行连接,一旦验证和连接成功完成后,服务器和客户端就会打开相关通道进行数据传送等操作。2)新建用户 通过增加用户以允许合法的客户能够通过客户端成功连接服务器以获取所需信息和数据。主要为用户创建以下信息:用户名、登录密码及登录的初始根目录。通过将信息加入UserNode的结构中记录可以访问的用户。通过在Adduser类中调用AlienFTPServerCmd中的AddUser函数来实现,把相应
10、账户、密码和路径通过参数传递,用AlienFTPServerCmd的对象ftpserver来通过AddUser完成,将User信息存入UserNode中。如下:AlienFTPServerCmd:UserNode*FtpUser=(*ftpserver).AddUser( m_username, m_password, m_userpath);3)侦听、启动连接 (以上结构同客户端) 新建用户后,通过开启服务,服务器端会对21端口进行不断侦听,当有相应客户端请求时,就会打开命令通道,当有数据传送时,再通过数据通道进行数据连接以传输文件等。该功能如以上所说,通过在主对话框类ALienFTPSer
11、verDlg调用OnStart,并使用线程ThreadStart来实现侦听和连接。以下为OnStart()函数:void CALienFTPServerDlg:OnStart() /开启服务器 / TODO: Add your control notification handler code here UpdateData(true); /更新在主对话框上得到的数据 pThreadStart = (HANDLE)_beginthreadex(NULL, 0, ThreadStart, (void*)this, 0, &m_dwID);4)命令控制和响应模块 FTP协议模型如第五部分介绍,整个
12、服务器部分的主要命令控制和相应控制都在AlienFTPServerCmd类中实现,因此它是整个FTP的关键部分,而主要的处理命令都在一个while(1)的“死”循环中执行,通过对每个列出的命令如USER,PASS,MODE,TYPE,CWD,PWD等进行对应的处理,并利用pszCmd和SendReply传送客户端的请求指令和服务器端的响应,为了清晰准确地反映执行过程的指令传送和响应结果,利用一个字符串str记录pszCmd和SendReply信息并在服务器主对话框上显示,以下是处理命令控制的while循环部分操作:/主循环,不停地接受客户端发出的命令进行响应 while ( 1 ) 。 。 C
13、FtpServerEx-str1=pszCmd; /用str1记录pszCmd的信息,即客户端的请求命令 if( !strcmp( pszCmd, QUIT ) ) CFtpServerEx-SendReply( Client, 221 Goodbye.rn); /SendREply进行相应的处理表示 break; else if( !strcmp( pszCmd, USER ) ) if( Client-bIsLogged = true ) Client-bIsLogged = false; Client-User-SClient += -1; Client-User = NULL; if(
14、 !pszCmdArg ) CFtpServerEx-SendReply( Client, 501 Invalid number of arguments.rn); else if( CFtpServerEx-bAllowAnonymous & !strcasecmp( pszCmdArg, anonymous ) /处理匿名登录的情况 & CFtpServerEx-AnonymousUser ) Client-bIsLogged = true; Client-bIsAnonymous = true; CFtpServerEx-SendReply( Client, 230 User Logge
15、d In.rn); Client-User = CFtpServerEx-AnonymousUser; else Client-User= CFtpServerEx-SearchUserFromName(pszCmdArg); if( Client-User & Client-User-bIsEnabled = false ) Client-User = NULL; CFtpServerEx-SendReply( Client, 331 Password required for this user.rn); continue; else if( !strcmp( pszCmd, PASS )
16、 ) /处理PASS指令 if( Client-bIsAnonymous ) Client-bIsLogged = true; CFtpServerEx-SendReply( Client, 230 User Logged In.rn); else6、小结开始处理SOCKET网络编程时,确实无从着手,而且对MFC这一块很陌生,所以只好完成一些简单的基于对话框的操作,通过一定的适应逐渐了解相关方面的知识,应用也稍加点熟练。毕竟是对SOCKET等方面的知识了解不够,最初只能通过相关实例来了解具体问题的应用和相关网络编程方面的知识。总体来说,通过FTP的实现更加深了对网络协议尤其是FTP传输协议的了
17、解,从而从深层次掌握相关方面的知识和应用。FTP客户端设计说明书1、概述客户端感觉上要比服务器端简单一些,其实不然。就我的开发体会而言,服务器端只要侦听21号端口,若有请求就打开控制通道,然后等待用户的命令做出相应的响应,并打开数据通道进行数据传输(目录列表或者文件),除了某些命令(例如USER和PASS),大部分的命令都是独立的,这就使得服务器端并不会存在复杂的有限状态机,特别是当我们并不需要实现其所有命令的时候(实际上我们实现了15条命令,具体见协议说明书)。而客户端则是根据功能需求向服务器端发出各种指令来完成的,但是一些稍复杂的功能,例如目录的下载(或上传),服务器端并不提供这条命令,但
18、是我们可以通过发出一系列其服务器提供的命令的组合来达到这个效果,生成这个命令组合便是客户端需要做的事之一。这就有点像服务器提供少量“机器指令”,客户端则用这些少量的“机器指令”去“搭积木”来完成功能需求。除此之外,客户端还要提供更多的图形元素,例如本地和远程的文件目录列表等。在我们这个客户端的设计中,我们共完成了登陆,退出,列目录,下载/上传文件,下载/上传目录,删除文件/目录,新建目录,断点续传以及传输过程中状态计算(比如下载速度,下载时间和剩余时间、传输进度百分比的计算等等)等功能,并通过多线程的方法使得整个软件不至于在下载或上传时出现由于某个函数的繁忙计算而导致界面“死掉”的情况。由于我
19、们是并行开发的,即我完成客户端,同时另一些同学完成服务器端,所以我在开发时就没法用他们写的服务器端来进行测试。取而代之的是,我用了主流的FTP服务器端软件Serv-U来测试我写的客户端。2、开发目标设计并实现一个符合标准FTP协议规范的,拥有登陆,退出,列目录,下载/上传文件,下载/上传目录,删除文件/目录,新建目录,断点续传以及传输过程中状态计算等功能的FTP客户端软件。二、参考文档协议说明书Visual C+ 6.0网络及Internet开发指南Visual C+网络程序设计实例详解Visual C+网络高级编程三、开发工具和平台Visual C+ 6.0,在Windows XP SP3下
20、开发。之所以选择Visual C+,是因为我本身对于VC的图形界面编程较为熟悉,相对而言对于java的图形界面的编程不是很熟悉(虽然java是以网络应用见长的)。而不同的语言提供的socket接口又大体上差不多,所以我选择了VC来作为开发工具。四、测试工具Serv-U 6.3。正如上文所说,我们是并行开发的,所以我的客户端只能通过连接现有的FTP服务端软件来测试,这里我选择了使用较广,操作方便的Serv-U。3、程序结构 1)类定义classspecificationCAlienFTPClientDlg主对话框类,用于图形界面的处理,通过调用AlienFTPCmd类提供的函数实现与服务器的连接
21、与交互。 AlienFTPCmdFTP命令处理类,该类主要负责实现功能需求的各种函数,供CAlienFTPClientDlg直接调用,同时也提供一些基本操作使CAlienFTPClientDlg可以通过这些操作的组合完成复杂的功能。 GetDirName用于弹出对话框让用户输入待新建的目录的名称 RmdDialog用于询问用户是否要删除一个远程的非空目录。 RestDialog本来打算用此对话框来在待传输的文件在接收方已存在时询问用户选择续传还是覆盖的对话框类,但是由于在线程中弹出对话框总是出错,所以后来就用在主对话框中加一单选框代替,寒假有时间我会尽量修正这个问题。 2)主要类接口2.1 C
22、AlienFTPClientDlg类classmemberfunctionFindLocFile根据指定的本地路径将该路径下的文件以列表的形式在主对话框的List Control中显示出来OnInsertFile被FindLocFile调用,用于向表示当前的本地目录的List Control插入一条目录/文件信息InsertServerFile用于向表示当前的远程目录的List Control插入一条目录/文件信息GetFileIcon用于查找本地文件/目录的图标GetFileIcon2用于根据远程文件/目录的的文件类型在本机查找对应类型注册的图标RemoveLocalDirectory删除本
23、地目录OnDblclkListLocal本地文件列表的双击响应事件函数,用户进入某个本地目录,并显示该目录里的文件列表OnButtonBacklocal本地文件列表的返回上一目录的响应函数OnButtonConnect与服务器连接的函数OnButtonLogoff与服务器断开的函数OnDblclkListServer远程文件列表的双击响应事件函数,用户进入某个远程目录,并显示该目录里的文件列表OnButtonBackserver远程文件列表的返回上一目录的响应函数OnDownload下载,在该函数中,将启动一个线程用于下载任务OnUpload上传,在该函数中,将启动一个线程用于上传任务OnLo
24、calDel删除本地某个文件OnServerDel删除远程某个文件OnLocNewDir在本地的当前路径下新建目录OnSerNewDir在远程的当前路径下新建目录OnRefreshLoc刷新本地文件列表OnRefreshSer刷新远程文件列表2.2 AlienFTPCmd类classmemberfunctionOpen打开命令通道Close关闭命令通道Order向服务器端发送一条命令Response接受服务器端的响应Command向服务器端发送一条命令并接受回应GetFileItem从服务器端发回的包含列表信息的数据中读取一条文件/目录信息List向服务器端发送LIST命令并接受服务器端在数据
25、通道传输过来的文件列表数据MoveFile用于单个文件的上传/下载LogOn登陆服务器LogOff断开服务器线程定义ThreadspecificationThreadTime定时器线程,用于下载/上传的已用时间,剩余时间的计算和显示ThreadStatus状态量计算线程,用于显示当前传输文件名,已传输大小,平均下载速度,下载进度的计算和显示ThreadDownload下载线程,根据下载的是文件或目录进行相应的下载任务,在下载目录的时候,该线程还负责传输队列的显示和更新ThreadUpload上传线程,根据上传的是文件或目录进行相应的上传任务,在上传目录的时候,该线程还负责传输队列的显示和更新5
26、、功能模块详细设计5)登陆登陆相对而言其他功能要简单得多,流程如下: 打开命令通道相应代码: m_Ctrlsok = socket(AF_INET,SOCK_STREAM,0); /建立套接字失败,则返回false if (m_Ctrlsok = SOCKET_ERROR) nRet = closesocket(m_Ctrlsok); :WSACleanup(); return FALSE; /定义结构体sockaddr_in,用于设置地址信息。 struct sockaddr_in sockAddr; /将一个点间隔地址转换成一个in_addr sockAddr.sin_addr.S_un.
27、S_addr= inet_addr(serverhost.GetBuffer(serverhost.GetLength(); /设置协议族,在socket编程中只能是AF_INET sockAddr.sin_family=AF_INET; /将主机的无符号短整形数转换成网络字节顺序 sockAddr.sin_port=htons(serverport); /连接服务器 if(0 != (connect(m_Ctrlsok, (sockaddr*)&sockAddr,sizeof(sockAddr) return FALSE; 这样,控制通道就打开了,向服务器端发命令时使用如下语句(命令在out
28、putstring中):send(m_Ctrlsok,outputstring.GetBuffer(outputstring.GetLength(),outputstring.GetLength(), 0);接受服务器端回应使用如下语句:recv(m_Ctrlsok, readbuf, 1024, 0);通过服务器端回应的前三位的回应码(详见协议说明书)来判断命令执行情况,即成功或失败。2)退出向服务器端发送“QUIT”命令即可。3)列目录列文件目录稍复杂点,但实际上也很简单,其复杂部分在于将服务器发过来的文件信息数据进行解析并得到相应的文件名,文件类型,文件大小,创建时间和权限等,处理流程如
29、下: 其中发送命令于接受回应已在上文解释过,下面我们探讨关于PORT命令、LIST命令和侦听的socket。在发送PORT命令之前要确保本地已经创建好了侦听socket,然后将该socket的地址和端口号转换为xxx,xxx,xxx,xxx,xxx,xxx(其中前四个xxx是将ip地址中的点换成逗号,后两个xxx实际上分别是port/256和port%256)作为PORT命令的参数发送给服务器端,服务器收到该条命令后会创建一个socket来连接我们已经在侦听的socket,这样,数据通道就建立好了。接着发送一条LIST命令,客户端便将当前远程目录下的文件信息从数据通道发送过来,我们对接受到的数
30、据进行解析便可以得到每个文件/目录具体的信息,如文件名,文件类型,文件大小,创建时间和权限等。侦听socket的创建如上文所述,其用于侦听要涉及到有关socket的另外三个函数,分别是getsockname、bind和listen。int PASCAL FAR getsockname( SOCKET s, struct sockaddr FAR* name,int FAR* namelen); getsockname()函数用于获取一个套接口的名字。它用于一个已捆绑或已连接套接口s,本地地址将被返回。本调用特别适用于如下情况:未调用bind()就调用了connect(),这时唯有getsockname()调用可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。int PASCAL FAR bind(SOCKET
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1