ftp实验报告

网络程序设计

实验报告

                                              

实验名称:___基于FTP的文件传输系统 _______

实验类型:______设计型实验___    _______

指导教师:______ _________________

专业班级:______ ______________

姓    名:______ ___________

学    号:_    ________

电子邮件:______ _____

实验地点:_______9-211_______________

期:       2013   年   5   月  7   

实验成绩:__________________________

基于FTP的文件传输系统设计

摘要:文件传送是各种计算机网络实现的基本功能,文件传送协议是一种最基本的应用层协议按照客户/服务器的模式进行工作,提供交互式的访问,是INTERNET使用最广泛的协议之一。文件传输协议的简单设计与实现建立在计算机网络实验环境TCP/IP网络体系结构之上,使用socket编程接口编写两个程序,分别为客户程序(client)和服务器程序(server),利用了已有网络环境设计并实现简单应用层协议。

关键字:


目录

一.问题描述及设计思路. 4

(一)问题描述. 5.

(二)设计思路. 6

(三)实验环境 2

二.详细设计过程. 4

三,结论及体会. 8

3.1客户端的实现. 8

3.2服务端的实现. 13

四.附录. 20

4.1附录A 20

4.2 附录B 20

5.参考文献. 22

一.问题描述及设计思路

(一)   问题描述

服务器端:

1.指定一个目录作为FTP目录(之后所有的文件操作均在此目录下进行)

2.接收客户端的命令请求并提供相应的服务。

客户端:完成与FTP用户的人机界面,以命令的方式向服务器请求服务,主要的命令有:

1.CONNECTIP地址端口号

2.     指定FTP服务器的IP地址和端口号,连接到服务器。

响应:连接成功/失败。

3.LOGIN用户名

4.     用户登录(假设在服务器端有用户管理功能)

5.响应:若用户不存在,提示“无此用户”;若用户存在,提示“密码:”;用户输入密码后,若正确提示“登录成功”;若不成功,提示“密码错”。

6.3.DIR

7.显示FTP服务器的FTP目录的文件列表(假设无子目录)

8.响应:目录列表(只要文件名即可)。

9.4.GET文件名

10.    将FTP目录下的文件下载到本地,支持断点续传

11.    响应:传送成功/失败/无此文件。

12.    5.PUT文件名

13.    将本地文件上传到FTP目录,支持断点续传

14.    响应:传送成功/失败/无此文件。

15.    6.HELP(客户端和服务器端均有该功能)

 

(二)设计思路

1.       FTP协议基础

FTP 是 TCP/IP 协议组中的协议之一该协议是Internet文件传送的基础它由一系列规格说明文档组成目标是提高文件的共享性提供非直接使用远程计算机使存储介质对用户透明和可靠高效地传送数据。简单的说FTP就是完成两台计算机之间的拷贝从远程计算机拷贝文件至自己的计算机上或从自己计算机中拷贝至远程计算机上。在TCP/IP协议中FTP标准命令TCP端口号为21Port方式数据端口为20。

2.       网络文件传输原理

在通信的时候主要可以分为两个部分一个部分是用于传送控制信息例如发送文件的请求文件的名称、大小等采用MFC所提供的CSocket类的串行化技术来实现而另一个部分就是文件的传输部分对于文件的传输由于数据量相对来说比较大使用两个线程一个用于发送一个用于接收。这两个部分在具体实现的时候分别建立有自己的套接字。FTP编程基础 3. FTP传输原理 FTP协议使用两条单独的TCP连接一条专用于发送FTP命令另一条则专用于传递数据。初始建立连接时服务器在21号端口上接收来自客户端的命令连接。当需要传送数据时文件列表、文件数据等客户端向服务器发出Port命令并进入监听状态等待来自服务器的数据连接请求。

16.          实验环境

本次实验采用MFC工程工程采用MFC对话框界面工程名称为MyServer和FTPSOCKET。

17.          详细设计过程

1、总体功能模块图

(1)服务器端

服务器端功能说明:

① 线程控制:线程类别:选择线程类别,单线程或多线程传送文件

线程数设置:若选择多线程,可自行设置线程数目,

若选择单线程,则默认线程数目为1

② 启动服务器:设置好线程控制后,点击“启动”按钮启动服务器端

③ 选取文件:选择要发送给客户端的文件

④ 发送文件:获取要发送文件属性:获取到要传送给客户端的文件的基本属性,包括:文件名,大小(B),已发送多少(B),文件路径发送进度显示:显示给客户端发送文件的进度

⑤ ChatWithServer:启动采用UDP协议的简单聊天系统,用来与客户端进行通讯(客户端和服务器端都得启动,否则无法收到)

⑥ Help:显示服务器端使用说明

(2)客户端

客户端功能说明:

① 握手服务器:输入服务器端IP地址,点击“握手服务器”按钮,获取服务器端启动的线程数

② 连接服务器:与服务器进行连接

③ 接收文件:获取接收文件属性:获取到要接收的服务器端传来的文件的基本属性,包括:文件名,大小(B),已接收多少(B),文件存储路径接收进度显示:显示已接收文件的进度

④ ChatWithServer:启动采用UDP协议的简单聊天系统,用来与客户端进行通讯(客户端和服务器端都得启动,否则无法收到)

⑤ 获取IP地址:获取本机的主机名及IP地址

⑥ HELP:显示客户端使用说明

2、系统结构

3、主要功能流程图

(1)文件传输流程图

2、用例描述

(1)文件传送用例描述

功能设计及主要代码

服务器端 取得本地地址:

//   sprintf( g_szLocalAddr,"%s",GetLocalAddress() );

   if ((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

      WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("错误:Failed to get a socket %d\n", WSAGetLastError());

      WSACleanup();

      return 0;

   }

   inetAddr.sin_family = AF_INET;

   inetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

   inetAddr.sin_port = htons(server->m_Port);//FTP_PORT);

   if (bind(sListen, (PSOCKADDR) &inetAddr, sizeof(inetAddr)) == SOCKET_ERROR)

   {

      printf("错误:bind() failed with error %d\n", WSAGetLastError());

      return 0;

   }

   if (listen(sListen, SOMAXCONN))

   {

      printf("错误:listen() failed with error %d\n", WSAGetLastError());

      return 0;

   }

   printf("Mini Ftpserver已经启动 \n");

   printf("Mini Ftpserver开始侦听 \n");

   if ((sAccept = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

      WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("错误:Failed to get a socket %d\n", WSAGetLastError());

      return 0;

   }

登录设置:

int CServer::LoginIn( LPSOCKET_INF pSocketInfo  )

{

    const char* szUserOK = "331 User name okay, need password.\r\n";

    const char* szLoggedIn = "230 User logged in, proceed.\r\n";

    int  nRetVal = 0;

    static char szUser[MAX_NAME_LEN], szPwd[MAX_PWD_LEN];

    LPSOCKET_INF pSI = pSocketInfo;

    // 取得登录用户名

    if( strstr(strupr(pSI->buffRecv),"USER") )

    {      

        sprintf(szUser,"%s",pSI->buffRecv+strlen("USER")+1);

        strtok( szUser,"\r\n");    

        // 响应信息

        sprintf(pSI->buffSend,"%s",szUserOK );

        if( SendRes(pSI) == -1 ) return -1;

        return USER_OK;

    }

    if( strstr(strupr(pSI->buffRecv),"PASS") || strstr(pSI->buffRecv,"pass") )

    {

        sprintf(szPwd,"%s",pSI->buffRecv+strlen("PASS")+1 );

        strtok( szPwd,"\r\n");

        // 判断用户名跟口令正确性

        if( stricmp( szPwd,m_Password) || stricmp(szUser,m_User) )

        {

            sprintf(pSI->buffSend,"530 User %s cannot log in.\r\n",szUser );

            printf("User %s cannot log in\n",szUser );

            nRetVal = LOGIN_FAILED;

        }

        else

        {

            sprintf(pSI->buffSend,"%s",szLoggedIn);

            printf("User %s logged in\n",szUser );

            nRetVal = LOGGED_IN;

        }

        if( SendRes( pSI ) == -1 )

            return -1;

    }

    return nRetVal;

}

客户端:

登录到服务器

BOOL CFTPCommandProcessor::LogOnToServer(CString hostname,int hostport,CString username, CString password, CString acct, CString fwhost,CString fwusername, CString fwpassword,int fwport,int logontype)

{

       int port,logonpoint=0;

       const int LO=-2, ER=-1;

       CString buf,temp;

       const int NUMLOGIN=9; // 支持9种不同的登录方式

       int logonseq[NUMLOGIN][100] = {

              // 下面的数组保存了针对不同防火墙的登录序列

              {0,LO,3, 1,LO,6, 2,LO,ER}, // 没有防火墙

              {3,6,3, 4,6,ER, 5,ER,9, 0,LO,12, 1,LO,15, 2,LO,ER}, // 主机名

              {3,6,3, 4,6,ER, 6,LO,9, 1,LO,12, 2,LO,ER}, // USER after logon

              {7,3,3, 0,LO,6, 1,LO,9, 2,LO,ER}, //proxy OPEN

              {3,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, // Transparent

              {6,LO,3, 1,LO,6, 2,LO,ER}, // USER with no logon

              {8,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, //USER fireID@remotehost

              {9,ER,3, 1,LO,6, 2,LO,ER}, //USER remoteID@remotehost fireID

              {10,LO,3, 11,LO,6, 2,LO,ER} // USER remoteID@fireID@remotehost

       };

       if(logontype<0||logontype>=NUMLOGIN) return FALSE; // illegal connect code

       if(!logontype) {

              temp=hostname;

              port=hostport;

       }

       else {

              temp=fwhost;

              port=fwport;

       }

       if(hostport!=21) hostname.Format(hostname+":%d",hostport); // 如果端口不是默认端口21,则设定端口

       if(!OpenControlChannel(temp,port)) return false;

       if(!FTPcommand("")) return FALSE; // 获得连接服务器初始化信息

       // 获得登录类型

       while(1) {

              switch(logonseq[logontype][logonpoint]) {

              case 0:

                     temp="USER "+username;

                     break;

              case 1:

                     temp="PASS "+password;

                     break;

              case 2:

                     temp="ACCT "+acct;

                     break;

              case 3:

                     temp="USER "+fwusername;

                     break;

              case 4:

                     temp="PASS "+fwpassword;

                     break;

              case 5:

                     temp="SITE "+hostname;

                     break;

              case 6:

                     temp="USER "+username+"@"+hostname;

                     break;

              case 7:

                     temp="OPEN "+hostname;

                     break;

              case 8:

                     temp="USER "+fwusername+"@"+hostname;

                     break;

              case 9:

                     temp="USER "+username+"@"+hostname+" "+fwusername;

                     break;

              case 10:

                     temp="USER "+username+"@"+fwusername+"@"+hostname;

                     break;

              case 11:

                     temp="PASS "+password+"@"+fwpassword;

                     break;

              }

              // 发出命令,获得响应

              if(!WriteStr(temp)) return FALSE;

              if(!ReadStr()) return FALSE;

              // 只有这些响应是合法的

              if(m_fc!=2&&m_fc!=3) return FALSE;

              logonpoint=logonseq[logontype][logonpoint+m_fc-1]; //get next command from array

              switch(logonpoint) {

              case ER: // 出现错误

                     m_retmsg.LoadString(IDS_FTPMSG1);

                     return FALSE;

              case LO: // L0表示成功登录

                     return TRUE;

              }

       }

}

下载算法描述:

1、创建二进制输入流用来接收socket收到的数据并封装

2、在下载目录下创建该文件名的文件

3、将文件封装到输出流进行输入

4、客户端进行接收

代码:

   // 打开本地文件

   if(!datafile.Open(localfile,(get?CFile::modeWrite|CFile::modeCreate:CFile::modeRead))) {

       m_retmsg.LoadString(IDS_FTPMSG4);

       return FALSE;

   }

   if(!FTPcommand("TYPE I")) return FALSE; // 请求二进制传输

   if(pasv) { // 建立被动传输方式

       if(!FTPcommand("PASV")) return FALSE;

       // 分析出服务器传回的临时IP地址以及端口号

       if((i=m_retmsg.Find("("))==-1||(j=m_retmsg.Find(")"))==-1) return FALSE;

       temp=m_retmsg.Mid(i+1,(j-i)-1);

       i=temp.ReverseFind(',');

       serversock=atol(temp.Right(temp.GetLength()-(i+1))); //get ls byte of server socket

       temp=temp.Left(i);

       i=temp.ReverseFind(',');

       serversock+=256*atol(temp.Right(temp.GetLength()-(i+1))); // add ms byte to server socket

       rhost=temp.Left(i);

       while(1) { // 将逗号转化成点

          if((i=rhost.Find(","))==-1) break;

          rhost.SetAt(i,'.');

       }

   }

   else { // 设置主动的传输模式

       m_retmsg.LoadString(IDS_FTPMSG6);

       //获取本地的ip地址,发送到服务器

       if(!m_Ctrlsok->GetSockName(lhost,localsock)) return FALSE;;

       while(1) { // 将IP地址中的点转化成逗号

          if((i=lhost.Find("."))==-1) break;

          lhost.SetAt(i,',');

       }

       // 创建本地侦听进程

       if((!sockSrvr.Create(0,SOCK_STREAM,NULL))||(!sockSrvr.Listen())) return FALSE;

       if(!sockSrvr.GetSockName(temp,localsock)) return FALSE;// get the port that MFC chose

       // 将端口转化成2字节,然后加入到本地IP地址中

       lhost.Format(lhost+",%d,%d",localsock/256,localsock%256);

       if(!FTPcommand("PORT "+lhost)) return FALSE;// 发送端口到服务器

   }

   // 发送 RETR/STOR 命令到服务器

   if(!WriteStr((get?"RETR ":"STOR ")+remotefile)) return FALSE;

   if(pasv) {// 如果是PASV模式,则创建socket并初始化外部数据连接,即数据传输通道

       if(!datachannel.Create()) {

          m_retmsg.LoadString(IDS_FTPMSG6);

          return FALSE;

       }

       datachannel.Connect(rhost,serversock); // 试图异步连接服务器

   }

   if(!ReadStr()||m_fc!=1) return FALSE; // 获得服务器响应

   if(!pasv&&!sockSrvr.Accept(datachannel)) return FALSE; // 接收从服务器来的内部绑定数据

   // 连接成功,然后进行阻塞式数据传输

   if((!datachannel.AsyncSelect(0))||(!datachannel.IOCtl(FIONBIO,&lpArgument))) {

       m_retmsg.LoadString(IDS_FTPMSG6);

       return FALSE;

   }

   while(1) { // 开始传输数据

       TRY {

          if(get) {

              if(!(num=datachannel.Receive(cbuf,BUFSIZE,0))||num==SOCKET_ERROR) break; // (EOF||network error)

              else datafile.Write(cbuf,num);

          }

          else {

              if(!(numread=datafile.Read(cbuf,BUFSIZE))) break; //EOF

              if((numsent=datachannel.Send(cbuf,numread,0))==SOCKET_ERROR) break;

              // 如果发送出去的字节少于从文件读取的字节,则调整发送指针,以使得数据发送正确

              if(numread!=numsent) datafile.Seek(numsent-numread,CFile::current);

          }

       }

       CATCH (CException,e) {

          m_retmsg.LoadString(IDS_FTPMSG5);

          return FALSE;

       }

       END_CATCH

   }

   datachannel.Close();

   datafile.Close();

   if(!FTPcommand("")) return FALSE; // 检查从服务器发送的传输结果信息

   return TRUE; // 传输成功

}

运行结果:

18.          总结及体会

刚开始接触网络编程时里面复杂的函数名称和生疏的原函数模型使得对程序学的学习很是迷茫经常去想那些复杂的代码代表的都是什么意思其实那些只是一个名称知道如何使用就行了慢慢可以开始揣度教材的程序试着编写接下面的内容通过不断的编译和运行对于许多的错误的检测,做多种可能的尝试改动之后继而得到的正确结果反过来思考整个程序的运行流程在不断的更正中使自己不断地提高。当然,这份成果也离不开老师和同学的帮助和支持,在这里着重地表示感谢.

附录:

源程序

客户端

/*/////////////////////////////////////////////////////////////////////

FTPclient.cpp (c) GDI 1999

V1.0.0 (10/4/99)

Phil Anderson. philip@gd-ind.com

Simple FTP client functionality. If you have any problems with it,

please tell me about them (or better still e-mail me the fixed

code). Please feel free to use this code however you wish, although

if you make changes please put your name in the source & comment what

you did.

Nothing awesome going on here at all (all sockets are used in

synchronous blocking mode), but it does the following

things WinInet doesn't seem to:

* Supports loads of different firewalls (I think, I don't

  have access to all types so they haven't all been fully

  tested yet)

* Allows you to execute any command on the FTP server

* Adds 10K to your app install rather than 1Mb #;-)

Functions return TRUE if everything went OK, FALSE if there was an,

error. A message describing the outcome (normally the one returned

from the server) will be in m_retmsg on return from the function.

There are a few error msgs in the app's string table that you'll

need to paste into your app, along with this file & FTPclient.h

If you created your app without checking the "Use Windows Sockets"

checkbox in AppWizard, you'll need to add the following bit of code

to you app's InitInstance()

if(!AfxSocketInit())

{

       AfxMessageBox("Could not initialize Windows Sockets!");

       return FALSE;

}

To use:

1/ Create an object of CFTPCommandProcessor.

2/ Use LogOnToServer() to connect to the server. Any arguments

not used (e.g. if you're not using a firewall), pass an empty

string or zero for numeric args. You must pass a server port

number, use the FTP default of 21 if you don't know what it is.

3/ Use MoveFile() to upload/download a file, 1st arg is local file

path, 2nd arg is remote file path, 3rd arg is TRUE for a PASV

connection (required by some firewalls), FALSE otherwise, 4th arg

is TRUE to upload, FALSE to download file. MoveFile only works in

synchronous mode (ie the function will not return 'till the transfer

is finished). File transfers are always of type BINARY.

4/ You can use FTPcommand() to execute FTP commands (eg

FTPcommand("CWD /home/mydir") to change directory on the server),

note that this function will return FALSE unless the server response

is a 200 series code. This should work fine for most FTP commands,

otherwise you can use WriteStr() and ReadStr() to send commands &

interpret the response yourself. Use LogOffServer() to disconnect

when done.

/////////////////////////////////////////////////////////////////////*/

#include "stdafx.h"

#include "MyFTPClient.h"

#include "FTPCommandProcessor.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////

//构造函数,变量初始化

CFTPCommandProcessor::CFTPCommandProcessor()

{

       m_pCtrlsokfile=NULL;

       m_pCtrlTxarch=NULL;

       m_pCtrlRxarch=NULL;

       m_Ctrlsok=NULL;

}

CFTPCommandProcessor::~CFTPCommandProcessor()

{

       CloseControlChannel();

}

//////////////////////////////////////////////////////////////////////

// Public Functions

//////////////////////////////////////////////////////////////////////

// 登录到服务器

BOOL CFTPCommandProcessor::LogOnToServer(CString hostname,int hostport,CString username, CString password, CString acct, CString fwhost,CString fwusername, CString fwpassword,int fwport,int logontype)

{

       int port,logonpoint=0;

       const int LO=-2, ER=-1;

       CString buf,temp;

       const int NUMLOGIN=9; // 支持9种不同的登录方式

       int logonseq[NUMLOGIN][100] = {

              // 下面的数组保存了针对不同防火墙的登录序列

              {0,LO,3, 1,LO,6, 2,LO,ER}, // 没有防火墙

              {3,6,3, 4,6,ER, 5,ER,9, 0,LO,12, 1,LO,15, 2,LO,ER}, // 主机名

              {3,6,3, 4,6,ER, 6,LO,9, 1,LO,12, 2,LO,ER}, // USER after logon

              {7,3,3, 0,LO,6, 1,LO,9, 2,LO,ER}, //proxy OPEN

              {3,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, // Transparent

              {6,LO,3, 1,LO,6, 2,LO,ER}, // USER with no logon

              {8,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, //USER fireID@remotehost

              {9,ER,3, 1,LO,6, 2,LO,ER}, //USER remoteID@remotehost fireID

              {10,LO,3, 11,LO,6, 2,LO,ER} // USER remoteID@fireID@remotehost

       };

       if(logontype<0||logontype>=NUMLOGIN) return FALSE; // illegal connect code

       if(!logontype) {

              temp=hostname;

              port=hostport;

       }

       else {

              temp=fwhost;

              port=fwport;

       }

       if(hostport!=21) hostname.Format(hostname+":%d",hostport); // 如果端口不是默认端口21,则设定端口

       if(!OpenControlChannel(temp,port)) return false;

       if(!FTPcommand("")) return FALSE; // 获得连接服务器初始化信息

       // 获得登录类型

       while(1) {

              switch(logonseq[logontype][logonpoint]) {

              case 0:

                     temp="USER "+username;

                     break;

              case 1:

                     temp="PASS "+password;

                     break;

              case 2:

                     temp="ACCT "+acct;

                     break;

              case 3:

                     temp="USER "+fwusername;

                     break;

              case 4:

                     temp="PASS "+fwpassword;

                     break;

              case 5:

                     temp="SITE "+hostname;

                     break;

              case 6:

                     temp="USER "+username+"@"+hostname;

                     break;

              case 7:

                     temp="OPEN "+hostname;

                     break;

              case 8:

                     temp="USER "+fwusername+"@"+hostname;

                     break;

              case 9:

                     temp="USER "+username+"@"+hostname+" "+fwusername;

                     break;

              case 10:

                     temp="USER "+username+"@"+fwusername+"@"+hostname;

                     break;

              case 11:

                     temp="PASS "+password+"@"+fwpassword;

                     break;

              }

              // 发出命令,获得响应

              if(!WriteStr(temp)) return FALSE;

              if(!ReadStr()) return FALSE;

              // 只有这些响应是合法的

              if(m_fc!=2&&m_fc!=3) return FALSE;

              logonpoint=logonseq[logontype][logonpoint+m_fc-1]; //get next command from array

              switch(logonpoint) {

              case ER: // 出现错误

                     m_retmsg.LoadString(IDS_FTPMSG1);

                     return FALSE;

              case LO: // L0表示成功登录

                     return TRUE;

              }

       }

}

// 退出服务器

void CFTPCommandProcessor::LogOffServer() {

       WriteStr("QUIT");

       CloseControlChannel();

}

// 发送命令到服务器

BOOL CFTPCommandProcessor::FTPcommand(CString command) {

       if(command!=""&&!WriteStr(command)) return FALSE;

       if((!ReadStr())||(m_fc!=2)) return FALSE;

       return TRUE;

}

// 上载或者下载文件

BOOL CFTPCommandProcessor::MoveFile(CString remotefile, CString localfile,BOOL pasv,BOOL get)

{

       CString lhost,temp,rhost;

       UINT localsock,serversock,i,j;

       CFile datafile;

       CSocket sockSrvr;

       CAsyncSocket datachannel;

       int num,numread,numsent;

       const int BUFSIZE=4096;

       char cbuf[BUFSIZE];

       DWORD lpArgument=0;

      

       // 打开本地文件

       if(!datafile.Open(localfile,(get?CFile::modeWrite|CFile::modeCreate:CFile::modeRead))) {

              m_retmsg.LoadString(IDS_FTPMSG4);

              return FALSE;

       }

       if(!FTPcommand("TYPE I")) return FALSE; // 请求二进制传输

       if(pasv) { // 建立被动传输方式

              if(!FTPcommand("PASV")) return FALSE;

              // 分析出服务器传回的临时IP地址以及端口号

              if((i=m_retmsg.Find("("))==-1||(j=m_retmsg.Find(")"))==-1) return FALSE;

              temp=m_retmsg.Mid(i+1,(j-i)-1);

              i=temp.ReverseFind(',');

              serversock=atol(temp.Right(temp.GetLength()-(i+1))); //get ls byte of server socket

              temp=temp.Left(i);

              i=temp.ReverseFind(',');

              serversock+=256*atol(temp.Right(temp.GetLength()-(i+1))); // add ms byte to server socket

              rhost=temp.Left(i);

              while(1) { // 将逗号转化成点

                     if((i=rhost.Find(","))==-1) break;

                     rhost.SetAt(i,'.');

              }

       }

       else { // 设置主动的传输模式

              m_retmsg.LoadString(IDS_FTPMSG6);

              //获取本地的ip地址,发送到服务器

              if(!m_Ctrlsok->GetSockName(lhost,localsock)) return FALSE;;

              while(1) { // 将IP地址中的点转化成逗号

                     if((i=lhost.Find("."))==-1) break;

                     lhost.SetAt(i,',');

              }

              // 创建本地侦听进程

              if((!sockSrvr.Create(0,SOCK_STREAM,NULL))||(!sockSrvr.Listen())) return FALSE;

              if(!sockSrvr.GetSockName(temp,localsock)) return FALSE;// get the port that MFC chose

              // 将端口转化成2字节,然后加入到本地IP地址中

              lhost.Format(lhost+",%d,%d",localsock/256,localsock%256);

              if(!FTPcommand("PORT "+lhost)) return FALSE;// 发送端口到服务器

       }

       // 发送 RETR/STOR 命令到服务器

       if(!WriteStr((get?"RETR ":"STOR ")+remotefile)) return FALSE;

       if(pasv) {// 如果是PASV模式,则创建socket并初始化外部数据连接,即数据传输通道

              if(!datachannel.Create()) {

                     m_retmsg.LoadString(IDS_FTPMSG6);

                     return FALSE;

              }

              datachannel.Connect(rhost,serversock); // 试图异步连接服务器

       }

       if(!ReadStr()||m_fc!=1) return FALSE; // 获得服务器响应

       if(!pasv&&!sockSrvr.Accept(datachannel)) return FALSE; // 接收从服务器来的内部绑定数据

       // 连接成功,然后进行阻塞式数据传输

       if((!datachannel.AsyncSelect(0))||(!datachannel.IOCtl(FIONBIO,&lpArgument))) {

              m_retmsg.LoadString(IDS_FTPMSG6);

              return FALSE;

       }

       while(1) { // 开始传输数据

              TRY {

                     if(get) {

                            if(!(num=datachannel.Receive(cbuf,BUFSIZE,0))||num==SOCKET_ERROR) break; // (EOF||network error)

                            else datafile.Write(cbuf,num);

                     }

                     else {

                            if(!(numread=datafile.Read(cbuf,BUFSIZE))) break; //EOF

                            if((numsent=datachannel.Send(cbuf,numread,0))==SOCKET_ERROR) break;

                            // 如果发送出去的字节少于从文件读取的字节,则调整发送指针,以使得数据发送正确

                            if(numread!=numsent) datafile.Seek(numsent-numread,CFile::current);

                     }

              }

              CATCH (CException,e) {

                     m_retmsg.LoadString(IDS_FTPMSG5);

                     return FALSE;

              }

              END_CATCH

       }

       datachannel.Close();

       datafile.Close();

       if(!FTPcommand("")) return FALSE; // 检查从服务器发送的传输结果信息

       return TRUE; // 传输成功

}

// 通过控制通道向服务器发送命令

BOOL CFTPCommandProcessor::WriteStr(CString outputstring)

{

       m_retmsg.LoadString(IDS_FTPMSG6); // pre-load "network error" msg (in case there is one) #-)

       TRY {

              m_pCtrlTxarch->WriteString(outputstring+"\r\n");

              m_pCtrlTxarch->Flush();

       }

       CATCH(CException,e) {

              return FALSE;

       }

       END_CATCH

       return TRUE;

}

// 获得服务器的响应

BOOL CFTPCommandProcessor::ReadStr()

{

       int retcode;

       if(!ReadStr2()) return FALSE;

       if(m_retmsg.GetLength()<4||m_retmsg.GetAt(3)!='-') return TRUE;

       retcode=atol(m_retmsg);

       while(1)

       { //处理多行服务器响应

              if(m_retmsg.GetLength()>3&&(m_retmsg.GetAt(3)==' '&&atol(m_retmsg)==retcode))

                     return TRUE;

              if(!ReadStr2())

                     return FALSE;

       }

}

//////////////////////////////////////////////////////////////////////

// Private functions

//////////////////////////////////////////////////////////////////////

// 从服务器控制通道获取一行响应

BOOL CFTPCommandProcessor::ReadStr2()

{

       TRY

       {

              if(!m_pCtrlRxarch->ReadString(m_retmsg))

              {

                     m_retmsg.LoadString(IDS_FTPMSG6);

                     return FALSE;

              }

       }

       CATCH(CException,e)

       {

              m_retmsg.LoadString(IDS_FTPMSG6);

              return FALSE;

       }

       END_CATCH

       if(m_retmsg.GetLength()>0) m_fc=m_retmsg.GetAt(0)-48; // get 1st digit of the return code (indicates primary result)

       return TRUE;

}

// 打开控制通道

BOOL CFTPCommandProcessor::OpenControlChannel(CString serverhost,int serverport)

{

       m_retmsg.LoadString(IDS_FTPMSG2);

       if(!(m_Ctrlsok=new CSocket)) return FALSE;

       if(!(m_Ctrlsok->Create())) return FALSE;

       m_retmsg.LoadString(IDS_FTPMSG3);

       if(!(m_Ctrlsok->Connect(serverhost,serverport))) return FALSE;

       m_retmsg.LoadString(IDS_FTPMSG2);

       if(!(m_pCtrlsokfile=new CSocketFile(m_Ctrlsok))) return FALSE;

       if(!(m_pCtrlRxarch=new CArchive(m_pCtrlsokfile,CArchive::load))) return FALSE;

       if(!(m_pCtrlTxarch=new CArchive(m_pCtrlsokfile,CArchive::store))) return FALSE;

       return TRUE;

}

// 关闭控制通道

void CFTPCommandProcessor::CloseControlChannel()

{

       if(m_pCtrlTxarch) delete m_pCtrlTxarch;

       m_pCtrlTxarch=NULL;

       if(m_pCtrlRxarch) delete m_pCtrlRxarch;

       m_pCtrlRxarch=NULL;

       if(m_pCtrlsokfile) delete m_pCtrlsokfile;

       m_pCtrlsokfile=NULL;

       if(m_Ctrlsok) delete m_Ctrlsok;

       m_Ctrlsok=NULL;

       return;

}

//列出文件列表

BOOL CFTPCommandProcessor::List()

{

       CString lhost,temp,rhost;

       UINT localsock,i;

       CFile datafile;

       CSocket sockSrvr;

       CAsyncSocket datachannel;

       int num, sum;

       const int BUFSIZE = 4096;

       DWORD lpArgument=0;

      

       m_buf.RemoveAll();

       m_buf.SetSize(BUFSIZE);

       if(!FTPcommand("TYPE I"))

              return FALSE; // 请求二进制模式

       m_retmsg.LoadString(IDS_FTPMSG6);

       // 获取本地IP地址

       if(!m_Ctrlsok->GetSockName(lhost,localsock))

              return FALSE;;

       while(1) {

              // 将点转化成逗号

              if((i=lhost.Find("."))==-1) break;

              lhost.SetAt(i,',');

       }

       if((!sockSrvr.Create(0, SOCK_STREAM, NULL))

                     || (!sockSrvr.Listen()))

              return FALSE;

       if(!sockSrvr.GetSockName(temp,localsock))

              return FALSE;

       lhost.Format(lhost+",%d,%d", localsock / 256, localsock % 256);

       if(!FTPcommand("PORT "+lhost))

              return FALSE;

       if(!WriteStr("LIST") )

              return FALSE;

       if(!ReadStr())

              return FALSE;

       if(!sockSrvr.Accept(datachannel))

              return FALSE;

       if((!datachannel.AsyncSelect(0)) ||

              (!datachannel.IOCtl(FIONBIO,&lpArgument))) {

              m_retmsg.LoadString(IDS_FTPMSG6);

              return FALSE;

       }

       sum = 0;

       while(1) { // 获得数据

              TRY {

                     if(!(num = datachannel.Receive(m_buf.GetData() + sum, BUFSIZE, 0))

                            || num == SOCKET_ERROR)

                            break;

                     TRACE("Received :%d\n", num);

                     Sleep(0);

                     sum += num;

                     m_buf.SetSize(sum + BUFSIZE);

              }

              CATCH (CException,e) {

                     m_retmsg.LoadString(IDS_FTPMSG5);

                     return FALSE;

              }

              END_CATCH

       }

       datachannel.Close();

}

void CFTPCommandProcessor::ProcessList()

{

}

BOOL CFTPCommandProcessor::GetLine(int& ndx)

{

       m_strLine.Empty();

       int nBytes = m_buf.GetSize();

       BOOL bLine = FALSE;

       while ( bLine == FALSE && ndx < nBytes )

       {

              char ch = (char)(m_buf.GetAt( ndx ));

              switch( ch )

              {

              case '\n': // 行尾

                     bLine = TRUE;

                     break;

              default:   // 其他情况

                     m_strLine += ch;

                     break;

              }

              ++ndx;

       }

       m_strLine = m_strLine.Left(m_strLine.GetLength() - 1);

       return bLine;

}

服务器端

// Server.cpp: implementation of the CServer class.

//

//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "MyFtpServer.h"

#include "Server.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////

UINT ProcessTreadIO( LPVOID lpParam ) ;

DWORD g_dwEventTotal = 0;

DWORD g_index;

WSAEVENT g_events[WSA_MAXIMUM_WAIT_EVENTS];

LPSOCKET_INF g_sockets[WSA_MAXIMUM_WAIT_EVENTS];

CRITICAL_SECTION g_cs; 

//char  g_szLocalAddr[MAX_ADDR_LEN];

BOOL  g_bLoggedIn;

CServer::CServer()

{

       m_Directory[0]='\0';

       m_Password[0]='\0';

       m_User[0]='\0';

       m_WelcomeInfor[0]='\0';

       m_bStop=FALSE;

}

CServer::~CServer()

{

}

UINT ServerThread(LPVOID lpParameter)

{

       SOCKET sListen, sAccept;

       SOCKADDR_IN inetAddr;

       DWORD dwFlags;

       DWORD dwRecvBytes;

       CServer * server =(CServer*)lpParameter;

   InitializeCriticalSection(&g_cs);

   // 先取得本地地址

//   sprintf( g_szLocalAddr,"%s",GetLocalAddress() );

   if ((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

      WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("错误:Failed to get a socket %d\n", WSAGetLastError());

         WSACleanup();

      return 0;

   }

   inetAddr.sin_family = AF_INET;

   inetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

   inetAddr.sin_port = htons(server->m_Port);//FTP_PORT);

   if (bind(sListen, (PSOCKADDR) &inetAddr, sizeof(inetAddr)) == SOCKET_ERROR)

   {

      printf("错误:bind() failed with error %d\n", WSAGetLastError());

      return 0;

   }

   if (listen(sListen, SOMAXCONN))

   {

      printf("错误:listen() failed with error %d\n", WSAGetLastError());

      return 0;

   }

   printf("Mini Ftpserver已经启动 \n");

   printf("Mini Ftpserver开始侦听 \n");

   if ((sAccept = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

      WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("错误:Failed to get a socket %d\n", WSAGetLastError());

      return 0;

   }

   //创建第一个手动重置对象

   if ((g_events[0] = WSACreateEvent()) == WSA_INVALID_EVENT)

   {

      printf("错误:WSACreateEvent failed with error %d\n", WSAGetLastError());

      return 0;

   }

   // 创建一个线程处理请求

   AfxBeginThread(ProcessTreadIO,(LPVOID)server);

 

   /*

   if (CreateThread(NULL, 0, ProcessTreadIO,(void*)server, 0, &dwThreadId) == NULL)

   {

      printf("错误:CreateThread failed with error %d\n", GetLastError());

      return 0;

   }

       */

   g_dwEventTotal = 1;

   while(!server->m_bStop)

   {

       //处理入站连接

      if ((sAccept = accept(sListen, NULL, NULL)) == INVALID_SOCKET)

      {

          printf("错误:accept failed with error %d\n", WSAGetLastError());

          return 0;

      }

      //回传欢迎消息

         if( !server->WelcomeInfo( sAccept ) ) break;

      //设置ftp根目录

         if( !SetCurrentDirectory( server->m_Directory) ) break;

         //操作临界区,防止出错

      EnterCriticalSection(&g_cs);

      //创建一个新的SOCKET_INF结构处理接受的数据socket.

      if ((g_sockets[g_dwEventTotal] = (LPSOCKET_INF)

                GlobalAlloc(GPTR,sizeof(SOCKET_INF))) == NULL)

      {

         printf("错误:GlobalAlloc() failed with error %d\n", GetLastError());

         return 0;

      }

      //初始化新的SOCKET_INF结构

         char buff[DATA_BUFSIZE]; memset( buff,0,DATA_BUFSIZE );

         g_sockets[g_dwEventTotal]->wsaBuf.buf = buff; 

         g_sockets[g_dwEventTotal]->wsaBuf.len = DATA_BUFSIZE;

      g_sockets[g_dwEventTotal]->s = sAccept;

      memset(&(g_sockets[g_dwEventTotal]->o),0, sizeof(OVERLAPPED));

      g_sockets[g_dwEventTotal]->dwBytesSend = 0;

      g_sockets[g_dwEventTotal]->dwBytesRecv = 0;

         g_sockets[g_dwEventTotal]->nStatus     = WSA_RECV;    // 接收

  

     //创建事件

      if ((g_sockets[g_dwEventTotal]->o.hEvent = g_events[g_dwEventTotal] =

          WSACreateEvent()) == WSA_INVALID_EVENT)

      {

         printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());

         return 0;

      }

      //发出接受请求

      dwFlags = 0;

      if (WSARecv(g_sockets[g_dwEventTotal]->s,

         &(g_sockets[g_dwEventTotal]->wsaBuf), 1, &dwRecvBytes, &dwFlags,

         &(g_sockets[g_dwEventTotal]->o), NULL) == SOCKET_ERROR)

      {

         if (WSAGetLastError() != ERROR_IO_PENDING)

         {

            printf("错误:WSARecv() failed with error %d\n", WSAGetLastError());

            return 0;

         }

      }

      g_dwEventTotal++;

         //离开临界区

      LeaveCriticalSection(&g_cs);

         //使第一个事件有信号。使工作者线程处理其他的事件

      if (WSASetEvent(g_events[0]) == FALSE)

      {

         printf("错误:WSASetEvent failed with error %d\n", WSAGetLastError());

         return 0;

      }

   }

   server->m_bStop=FALSE;

   return 0;

}

//工作者线程处理函数

UINT ProcessTreadIO(LPVOID lpParameter)

{

   DWORD dwFlags;

   LPSOCKET_INF pSI;

   DWORD dwBytesTransferred;

   DWORD i;

   CServer * server=(CServer*)lpParameter;

   //处理异步的WSASend, WSARecv等请求等

   while(TRUE)

   {

      if ((g_index = WSAWaitForMultipleEvents(g_dwEventTotal, g_events, FALSE,

                                      WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED)

      {

         printf("错误:WSAWaitForMultipleEvents failed %d\n", WSAGetLastError());

         return 0;

      }

     

      if ((g_index - WSA_WAIT_EVENT_0) == 0)

      {

         WSAResetEvent(g_events[0]);

         continue;

      }

      pSI = g_sockets[g_index - WSA_WAIT_EVENT_0];

      WSAResetEvent(g_events[g_index - WSA_WAIT_EVENT_0]);

      if (WSAGetOverlappedResult(pSI->s, &(pSI->o), &dwBytesTransferred,

                    FALSE, &dwFlags) == FALSE || dwBytesTransferred == 0)

      {

         printf("Closing socket %d\n", pSI->s);

         if (closesocket(pSI->s) == SOCKET_ERROR)

         {

            printf("错误:closesocket() failed with error %d\n", WSAGetLastError());

         }

         GlobalFree(pSI);

         WSACloseEvent(g_events[g_index - WSA_WAIT_EVENT_0]);

         // Cleanup g_sockets and g_events by removing the socket event handle

         // and socket information structure if they are not at the end of the

         // arrays.

         EnterCriticalSection(&g_cs);

         if ((g_index - WSA_WAIT_EVENT_0) + 1 != g_dwEventTotal)

            for (i = g_index - WSA_WAIT_EVENT_0; i < g_dwEventTotal; i++)

                     {

               g_events[i] = g_events[i + 1];

                        g_sockets[i] = g_sockets[i + 1];

            }

         g_dwEventTotal--;

         LeaveCriticalSection(&g_cs);

         continue;

      }

         // 已经有数据传递

         if( pSI->nStatus == WSA_RECV )

         {

                memcpy( &pSI->buffRecv[pSI->dwBytesRecv],pSI->wsaBuf.buf,dwBytesTransferred);

                pSI->dwBytesRecv += dwBytesTransferred;

                printf( "接受:%s\n",pSI->buffRecv);

                if( pSI->buffRecv[pSI->dwBytesRecv-2] == '\r'      // 要保证最后是\r\n

                            && pSI->buffRecv[pSI->dwBytesRecv-1] == '\n'

                            && pSI->dwBytesRecv > 2 ) 

                {                

                      if( !g_bLoggedIn )

                      {

                            if( server->LoginIn(pSI) == LOGGED_IN )

                                   g_bLoggedIn = TRUE;

                      }

                      else

                      {

                              if(server->DealCommand( pSI )==FTP_QUIT)

                              continue;

                      }

                      // 缓冲区清除

                      memset( pSI->buffRecv,0,sizeof(pSI->buffRecv) );

                      pSI->dwBytesRecv = 0;

                }

         }

         else

         {

                pSI->dwBytesSend += dwBytesTransferred;

         }

        

        // 继续接收以后到来的数据

         if(server->RecvReq( pSI ) == -1 )

                return -1;

   }

   return 0;

}

void CServer::SetSerConfigInfor(const char *user,const char *password,const char *directory,const char * welcomeinfor,UINT port)

{

       strcpy(m_User,user);

       strcpy(m_Password,password);

       strcpy(m_Directory,directory);

       strcpy(m_WelcomeInfor,welcomeinfor);

       m_Port=port;

}

// 由于只是简单的出现一个登录信息,直接用send就可以了

int CServer::SendRes( LPSOCKET_INF pSI )

{

       static DWORD dwSendBytes = 0;

       pSI->nStatus = WSA_SEND;

    memset(&(pSI->o), 0,sizeof(WSAOVERLAPPED));

    pSI->o.hEvent = g_events[g_index - WSA_WAIT_EVENT_0];

    pSI->wsaBuf.buf = pSI->buffSend + pSI->dwBytesSend;

    pSI->wsaBuf.len = strlen( pSI->buffSend ) - pSI->dwBytesSend;

    if (WSASend(pSI->s, &(pSI->wsaBuf), 1,&dwSendBytes,

                      0,&(pSI->o), NULL) == SOCKET_ERROR)

       {

        if (WSAGetLastError() != ERROR_IO_PENDING)

              {

                     printf("WSASend() failed with error %d\n", WSAGetLastError());

                     return -1;

        }

    }

       return 0;

}

//接受数据

int CServer::RecvReq( LPSOCKET_INF pSI )

{

       static DWORD dwRecvBytes = 0;

       pSI->nStatus = WSA_RECV;      

       DWORD dwFlags = 0;

       memset(&(pSI->o), 0,sizeof(WSAOVERLAPPED));

       pSI->o.hEvent = g_events[g_index - WSA_WAIT_EVENT_0];

       pSI->wsaBuf.len = DATA_BUFSIZE;

       if (WSARecv(pSI->s, &(pSI->wsaBuf), 1, &dwRecvBytes,

                &dwFlags,&(pSI->o), NULL) == SOCKET_ERROR)

       {

              if (WSAGetLastError() != ERROR_IO_PENDING)

              {

                 printf("WSARecv() failed with error %d\n", WSAGetLastError());

                 return -1;

              }

       }

       return 0;

}

//显示欢迎消息

BOOL CServer::WelcomeInfo( SOCKET s )

{

       char* szWelcomeInfo = "220 欢迎您登录到Mini FtpServer...\r\n";

//     char * szWelcomeInfo=m_WelcomeInfor;

       if( send( s,szWelcomeInfo,strlen(szWelcomeInfo),0 ) == SOCKET_ERROR )

       {

              printf("Ftp client error:%d\n", WSAGetLastError() );

              return FALSE;

       }

       // 刚进来,还没连接,故设置初始状态为false

       g_bLoggedIn = FALSE;

       return TRUE;

}

//登录函数

int CServer::LoginIn( LPSOCKET_INF pSocketInfo  )

{

       const char* szUserOK = "331 User name okay, need password.\r\n";

       const char* szLoggedIn = "230 User logged in, proceed.\r\n";

       int  nRetVal = 0;

       static char szUser[MAX_NAME_LEN], szPwd[MAX_PWD_LEN];

       LPSOCKET_INF pSI = pSocketInfo;

       // 取得登录用户名

       if( strstr(strupr(pSI->buffRecv),"USER") )

       {           

              sprintf(szUser,"%s",pSI->buffRecv+strlen("USER")+1);

              strtok( szUser,"\r\n");           

              // 响应信息

              sprintf(pSI->buffSend,"%s",szUserOK );

              if( SendRes(pSI) == -1 ) return -1;

              return USER_OK;

       }

       if( strstr(strupr(pSI->buffRecv),"PASS") || strstr(pSI->buffRecv,"pass") )

       {

              sprintf(szPwd,"%s",pSI->buffRecv+strlen("PASS")+1 );

              strtok( szPwd,"\r\n");

              // 判断用户名跟口令正确性

              if( stricmp( szPwd,m_Password) || stricmp(szUser,m_User) )

              {

                     sprintf(pSI->buffSend,"530 User %s cannot log in.\r\n",szUser );

                     printf("User %s cannot log in\n",szUser );

                     nRetVal = LOGIN_FAILED;

              }

              else

              {

                     sprintf(pSI->buffSend,"%s",szLoggedIn);

                     printf("User %s logged in\n",szUser );

                     nRetVal = LOGGED_IN;

              }

              if( SendRes( pSI ) == -1 )

                     return -1;

       }

       return nRetVal;

}

char* CServer::ConvertCommaAddress( char* szAddress, WORD wPort )

{

       char szPort[10];

       sprintf( szPort,"%d,%d",wPort/256,wPort%256 );

       char szIpAddr[20];

       sprintf( szIpAddr,"%s,",szAddress );

       int idx = 0;

       while( szIpAddr[idx] )

       {

              if( szIpAddr[idx] == '.' )

                     szIpAddr[idx] = ',';

              idx ++;

       }

       sprintf( szAddress,"%s%s",szIpAddr,szPort );

       return szAddress;

}

int CServer::ConvertDotAddress( char* szAddress, LPDWORD pdwIpAddr, LPWORD pwPort )

{

       int  idx = 0,i = 0, iCount = 0;

       char szIpAddr[MAX_ADDR_LEN]; memset( szIpAddr,0,sizeof(szIpAddr) );

       char szPort[MAX_ADDR_LEN];   memset( szPort,0,  sizeof(szPort)   );

       *pdwIpAddr = 0; *pwPort = 0;

       while( szAddress[idx]  )

       {

              if( szAddress[idx] == ',' )

              {

                     iCount ++;

                     szAddress[idx] ='.';

              }

              if( iCount < 4 )

                     szIpAddr[idx] = szAddress[idx];

              else

                     szPort[i++] =   szAddress[idx];

              idx++;

       }

       if( iCount != 5 ) return -1;

       *pdwIpAddr = inet_addr( szIpAddr );

       if( *pdwIpAddr  == INADDR_NONE ) return -1;

       char *pToken = strtok( szPort+1,"." );

       if( pToken == NULL ) return -1;

       *pwPort = (WORD)(atoi(pToken) * 256);

       pToken = strtok(NULL,".");

       if( pToken == NULL ) return -1;

       *pwPort += (WORD)atoi(pToken);

             

       return 0;

}

UINT CServer::FileListToString( char* buff, UINT nBuffSize,BOOL bDetails )

{

       FILE_INF   fi[MAX_FILE_NUM];

       int nFiles = GetFileList( fi, MAX_FILE_NUM, "*.*" );

       char szTemp[128];

       sprintf( buff,"%s","" );

       if( bDetails ) {

              for( int i=0; i<nFiles; i++) {

                     if( strlen(buff)>nBuffSize-128 )   break;

                     if(!strcmp(fi[i].szFileName,"."))  continue;

                     if(!strcmp(fi[i].szFileName,"..")) continue;

                     // 时间

                     SYSTEMTIME st;

                     FileTimeToSystemTime(&(fi[i].ftLastWriteTime), &st);

                     char  *szNoon = "AM";

                     if( st.wHour > 12 )

                     {

                            st.wHour -= 12;

                            szNoon = "PM";

                     }

                     if( st.wYear >= 20## )

                            st.wYear -= 2000;

                     else st.wYear -= 1900;

                     sprintf( szTemp,"%02u-%02u-%02u  %02u:%02u%s       ",

                                          st.wMonth,st.wDay,st.wYear,st.wHour,st.wMonth,szNoon );

                     strcat( buff,szTemp );

                     if( fi[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )

                     {

                            strcat(buff,"<DIR>");

                            strcat(buff,"          ");

                     }

                     else

                     {

                            strcat(buff,"     ");

                            // 文件大小

                            sprintf( szTemp,"% 9d ",fi[i].nFileSizeLow );

                            strcat( buff,szTemp );

                     }

                     // 文件名

                     strcat( buff,fi[i].szFileName );

                     strcat( buff,"\r\n");

              }

       }

       else

       {

              for( int i=0; i<nFiles; i++)

              {

                     if( strlen(buff) + strlen( fi[i].szFileName ) + 2 < nBuffSize )

                     {

                            strcat( buff, fi[i].szFileName );

                            strcat( buff, "\r\n");

                     }

                     else

                            break;

              }

       }

       return strlen( buff );

}

DWORD CServer::ReadFileToBuffer( const char* szFile, char *buff, DWORD nFileSize )

{

       DWORD  idx = 0;

       DWORD  dwBytesLeft = nFileSize;

       DWORD  dwNumOfBytesRead = 0;

       char lpFileName[MAX_PATH];

       GetCurrentDirectory( MAX_PATH,lpFileName );

       strcat( lpFileName,"\\" );

       strcat(lpFileName,szFile );

       HANDLE hFile = CreateFile( lpFileName,

                                                    GENERIC_READ,

                                                    FILE_SHARE_READ,

                                                    NULL,

                                                    OPEN_EXISTING,

                                                    FILE_ATTRIBUTE_NORMAL,

                                                    NULL );

       if( hFile != INVALID_HANDLE_VALUE )

       {

              while( dwBytesLeft > 0 )

              {

                     if( !ReadFile( hFile,&buff[idx],dwBytesLeft,&dwNumOfBytesRead,NULL ) )

                     {

                            printf("读文件出错.\n");

                            CloseHandle( hFile );

                            return 0;

                     }

                     idx += dwNumOfBytesRead;

                     dwBytesLeft -= dwNumOfBytesRead;

              }

              CloseHandle( hFile );

       }

       return idx;

}

DWORD CServer::WriteToFile( SOCKET s , const char* szFile )

{

       DWORD  idx = 0;

       DWORD  dwNumOfBytesWritten = 0;

       DWORD  nBytesLeft = DATA_BUFSIZE;

       char   buf[DATA_BUFSIZE];

       char   lpFileName[MAX_PATH];

       GetCurrentDirectory( MAX_PATH,lpFileName );

       strcat( lpFileName,"\\" );

       strcat(lpFileName,szFile );

       HANDLE hFile = CreateFile( lpFileName,

                                                    GENERIC_WRITE,

                                                    FILE_SHARE_WRITE,

                                                    NULL,

                                                    OPEN_ALWAYS,

                                                    FILE_ATTRIBUTE_NORMAL,

                                                    NULL );

       if( hFile == INVALID_HANDLE_VALUE )

       {

              printf("打开文件出错.\n");

              return 0;

       }

      

       while( TRUE )

       {

              int nBytesRecv = 0;

              idx = 0; nBytesLeft = DATA_BUFSIZE;

              while( nBytesLeft > 0 )

              {

                     nBytesRecv = recv( s,&buf[idx],nBytesLeft,0 );

                     if( nBytesRecv == SOCKET_ERROR )

                     {

                            printf("Failed to send buffer to socket %d\n",WSAGetLastError() );

                            return -1;

                     }

                     if( nBytesRecv == 0 ) break;

             

                     idx += nBytesRecv;

                     nBytesLeft -= nBytesRecv;

              }

              nBytesLeft = idx;   // 要写入文件中的字节数

              idx = 0;                 // 索引清0,指向开始位置

              while( nBytesLeft > 0 )

              {

                     // 移动文件指针到文件末尾

                     if( !SetEndOfFile(hFile) ) return 0;

                     if( !WriteFile( hFile,&buf[idx],nBytesLeft,&dwNumOfBytesWritten,NULL ) )

                     {

                            printf("写文件出错.\n");

                            CloseHandle( hFile );

                            return 0;

                     }

                     idx += dwNumOfBytesWritten;

                     nBytesLeft -= dwNumOfBytesWritten;

              }

              // 如果没有数据可接收,退出循环

              if( nBytesRecv == 0 ) break;

       }

       CloseHandle( hFile );

       return idx;

}

int CServer::CombindFileNameSize( const char* szFileName,char* szFileNS )

{

       // 假定文件的大小不超过4GB,只处理低位

       int nFileSize = -1;

       FILE_INF fi[1];

       int nFiles = GetFileList( fi,1,szFileName );

       if( nFiles != 1 ) return -1;

       sprintf( szFileNS, "%s<%d bytes>",szFileName,fi[0].nFileSizeLow );

       nFileSize = fi[0].nFileSizeLow;

       return nFileSize;

}

int   CServer::DataConn( SOCKET& s, DWORD dwIp, WORD wPort, int nMode )

{

       // 创建一个socket

       s = socket( AF_INET,SOCK_STREAM,0 );

       if( s == INVALID_SOCKET )

       {

               printf("Failed to get a socket %d\n", WSAGetLastError());

               return -1;

       }

       struct sockaddr_in inetAddr;

       inetAddr.sin_family = AF_INET;

       if( nMode == MODE_PASV )

       {

               inetAddr.sin_port = htons( wPort );

               inetAddr.sin_addr.s_addr = dwIp;

       }

       else

       {

              inetAddr.sin_port = htons( DATA_FTP_PORT );

              inetAddr.sin_addr.s_addr = inet_addr(GetLocalAddress());

       }

       BOOL optval = TRUE;

       if( setsockopt(s,SOL_SOCKET,SO_REUSEADDR,

                            (char*)&optval,sizeof(optval) ) == SOCKET_ERROR )

       {

              printf("Failed to setsockopt %d.\n",WSAGetLastError() );

              closesocket(s);

              return -1;

       }

       if( bind( s,(struct sockaddr*)&inetAddr,sizeof(inetAddr)) == SOCKET_ERROR )

       {

              printf("Failed to bind a socket %d.\n",WSAGetLastError() );

              closesocket(s);

              return -1;

       }

       if( MODE_PASV == nMode )

       {

              if( listen( s,SOMAXCONN ) == SOCKET_ERROR )

              {

                     printf("Failed to listen a socket %d.\n",WSAGetLastError() );

                     closesocket(s);

                     return -1;

              }

       }

       else if( MODE_PORT == nMode )

       {

              struct sockaddr_in addr;

              addr.sin_family = AF_INET;

              addr.sin_port   = htons( wPort );

              addr.sin_addr.s_addr   = dwIp;

              if( connect( s, (const sockaddr*)&addr,sizeof(addr) ) == SOCKET_ERROR )

              {

                     printf("Failed to connect a socket %d\n",WSAGetLastError() );

                     closesocket( s );

                     return -1;

              }

       }

       return 0;

}

int CServer::DataSend( SOCKET s, char* buff,int nBufSize )

{

       int nBytesLeft = nBufSize;

       int idx = 0, nBytes = 0;

       while( nBytesLeft > 0 ) {

              nBytes = send( s,&buff[idx],nBytesLeft,0);

              if( nBytes == SOCKET_ERROR )

              {

                     printf("Failed to send buffer to socket %d\n",WSAGetLastError() );

                     closesocket( s );

                     return -1;

              }

              nBytesLeft -= nBytes;

              idx += nBytes;

       }

       return idx;

}

int CServer::DataRecv( SOCKET s, const char* szFileName )

{

       return WriteToFile( s, szFileName );    

}

SOCKET CServer::DataAccept( SOCKET& s )

{

       SOCKET sAccept = accept( s ,NULL,NULL );

       if( sAccept != INVALID_SOCKET )

       {

              closesocket( s );

       }

       return sAccept;

}

int CServer::DealCommand( LPSOCKET_INF pSI )

{

       int nRetVal = 0;

       static SOCKET sAccept = INVALID_SOCKET;

       static SOCKET s       = INVALID_SOCKET;

       static BOOL   bPasv   = FALSE;

             

       char  szCmd[MAX_REQ_LEN];

       char  szCurrDir[MAX_PATH];

       strcpy( szCmd, pSI->buffRecv );

       if( strtok( szCmd," \r\n") == NULL ) return -1;

       strupr( szCmd );

       const char*  szOpeningAMode = "150 Opening ASCII mode data connection for ";

       static DWORD  dwIpAddr = 0;

       static WORD   wPort    = 0;

       // ?PORT n1,n2,n3,n4,n5,n6

       if( strstr(szCmd,"PORT") )

       {

              if( ConvertDotAddress( pSI->buffRecv+strlen("PORT")+1,&dwIpAddr,&wPort) == -1 )

                     return -1;

              const char*  szPortCmdOK    = "200 PORT Command successful.\r\n";

              sprintf(pSI->buffSend,"%s",szPortCmdOK );

              if( SendRes( pSI ) == -1 ) return -1;

              bPasv = FALSE;

              return CMD_OK;

       }

       if( strstr( szCmd,"PASV") )

       {

              if( DataConn( s, htonl(INADDR_ANY), PORT_BIND, MODE_PASV ) == -1 )

                     return -1;

              char *szCommaAddress = ConvertCommaAddress( GetLocalAddress(),PORT_BIND );

              sprintf( pSI->buffSend,"227 Entering Passive Mode (%s).\r\n",szCommaAddress );

              if( SendRes( pSI ) == -1 )

                     return -1;

           bPasv = TRUE;

              return PASSIVE_MODE;           

       }

       if( strstr( szCmd, "NLST") || strstr( szCmd,"LIST") )

       {

              if( bPasv ) sAccept = DataAccept( s );

              if( !bPasv )

                     sprintf(pSI->buffSend,"%s/bin/ls.\r\n",szOpeningAMode );

              else

                     strcpy(pSI->buffSend,"125 Data connection already open; Transfer starting.\r\n");         

             

              if( SendRes( pSI ) == -1 )

                     return -1;

              // 取得文件列表信息,并转换成字符串

              BOOL bDetails = strstr(szCmd,"LIST")?TRUE:FALSE;

              char buff[DATA_BUFSIZE];

              UINT nStrLen = FileListToString( buff,sizeof(buff),bDetails);

              if( !bPasv )

              {

                     if( DataConn( s,dwIpAddr,wPort,MODE_PORT ) == -1 )

                            return -1;

                     if( DataSend( s, buff,nStrLen ) == -1 )

                            return -1;

                     closesocket(s);

              }

              else

              {

                     DataSend( sAccept,buff,nStrLen );

                     closesocket( sAccept );

              }

              sprintf( pSI->buffSend,"%s","226 Transfer complete.\r\n" );

              if( SendRes( pSI ) == -1 )

                     return -1;

              return TRANS_COMPLETE;

       }

       if( strstr( szCmd, "RETR") )

       {

              if( bPasv ) sAccept = DataAccept(s);   

              char szFileNS[MAX_PATH];

              char *szFile = strtok( NULL," \r\n" );

              int nFileSize = CombindFileNameSize( szFile,szFileNS );

              if( nFileSize == -1  )

              {

                     sprintf( pSI->buffSend,"550 %s: 系统找不到指定的文件.\r\n",szFile);

                     if( SendRes( pSI ) == -1 )

                            return -1;

                     if( !bPasv ) closesocket( sAccept );

                     else closesocket( s );

                    

                     return CANNOT_FIND;

              }

              else

                     sprintf(pSI->buffSend,"%s%s.\r\n",szOpeningAMode,szFileNS);

              if( SendRes( pSI ) == -1 )

                     return -1;

      

              char* buff = new char[nFileSize];

              if( NULL ==buff )

              {

                     printf("Not enough memory error!\n");

                     return -1;

              }

              if( ReadFileToBuffer( szFile,buff, nFileSize ) == (DWORD)nFileSize )

              {

                     // 处理Data FTP连接

                     Sleep( 10 );

                     if( bPasv )

                     {

                            DataSend( sAccept,buff,nFileSize );

                            closesocket( sAccept );

                     }

                     else

                     {

                            if( DataConn( s,dwIpAddr,wPort,MODE_PORT ) == -1 )

                                   return -1;

                            DataSend( s, buff, nFileSize );

                            closesocket( s );

                     }

              }

              if( buff != NULL )

                     delete[] buff;

              sprintf( pSI->buffSend,"%s","226 Transfer complete.\r\n" );

              if( SendRes( pSI ) == -1 )

                     return -1;

             

                           

              return TRANS_COMPLETE;

       }

       if( strstr( szCmd, "STOR") )

       {

              if( bPasv ) sAccept = DataAccept(s);          

              char *szFile = strtok( NULL," \r\n" );

              if( NULL == szFile ) return -1;

              sprintf(pSI->buffSend,"%s%s.\r\n",szOpeningAMode,szFile);

              if( SendRes( pSI ) == -1 )

                     return -1;

              // 处理Data FTP连接

              if( bPasv )

                     DataRecv( sAccept,szFile );

              else

              {

                     if( DataConn( s,dwIpAddr,wPort,MODE_PORT ) == -1 )

                            return -1;

                     DataRecv( s, szFile );

              }

             

              sprintf( pSI->buffSend,"%s","226 Transfer complete.\r\n" );

              if( SendRes( pSI ) == -1 )

                     return -1;

                           

              return TRANS_COMPLETE;

       }

       if( strstr( szCmd,"QUIT" ) )

       {

              sprintf( pSI->buffSend,"%s","221 Good bye,欢迎下次再来.\r\n" );

              if( SendRes( pSI ) == -1 )

                     return -1;

             

              return FTP_QUIT;

       }

       if( strstr( szCmd,"XPWD" ) || strstr( szCmd,"PWD") )

       {

              GetCurrentDirectory( MAX_PATH,szCurrDir );

              sprintf( pSI->buffSend,"257 \"%s\" is current directory.\r\n",

                     RelativeDirectory(szCurrDir) );

              if( SendRes( pSI ) == -1 ) return -1;

              return CURR_DIR;

       }

       if( strstr( szCmd,"CWD" ) || strstr(szCmd,"CDUP") )

       {

              char *szDir = strtok( NULL,"\r\n" );

              if( szDir == NULL ) szDir = "\\";

              char szSetDir[MAX_PATH];

              if( strstr(szCmd,"CDUP") )

                     strcpy(szSetDir,"..");

               else

                     strcpy(szSetDir,AbsoluteDirectory( szDir ) );

              if( !SetCurrentDirectory( szSetDir ) )

              {

                     sprintf(szCurrDir,"\\%s",szSetDir);

                     sprintf( pSI->buffSend,"550 %s No such file or Directory.\r\n",

                                   RelativeDirectory(szCurrDir) );

                     nRetVal = CANNOT_FIND;

              }

              else

              {

                     GetCurrentDirectory( MAX_PATH,szCurrDir );

                     sprintf( pSI->buffSend,"250 Directory changed to /%s.\r\n",

                                   RelativeDirectory(szCurrDir) );

                     nRetVal = DIR_CHANGED;

              }

              if( SendRes( pSI ) == -1 ) return -1;

              return nRetVal;

       }

       if( strstr( szCmd,"SYST" ) )

       {

              sprintf( pSI->buffSend,"%s","215 Windows_NT Version 4.0\r\n");

              if( SendRes( pSI ) == -1 ) return -1;

              return OS_TYPE;

       }

       if( strstr( szCmd,"TYPE") )

       {

              char *szType = strtok(NULL,"\r\n");

              if( szType == NULL ) szType = "A";

              sprintf(pSI->buffSend,"200 Type set to %s.\r\n",szType );

              if( SendRes( pSI ) == -1 )

                     return -1;

              return CMD_OK;         

       }

       if( strstr( szCmd,"REST" ) )

       {

              sprintf( pSI->buffSend,"504 Reply marker must be 0.\r\n");

              if( SendRes( pSI ) == -1 )

                     return -1;

              return REPLY_MARKER;          

       }

       if( strstr( szCmd,"NOOP") )

       {

              sprintf( pSI->buffSend,"200 NOOP command successful.\r\n");

              if( SendRes( pSI ) == -1 )

                     return -1;

              return CMD_OK;         

       }

       //其余都是无效的命令

       sprintf(pSI->buffSend,"500 '%s' command not understand.\r\n",szCmd );

       if( SendRes( pSI ) == -1 ) return -1;   

       return nRetVal;

}

///////////////////////////////////////////////////////////////////////////////////////////

//其他函数

char* CServer::GetLocalAddress()

{

    struct in_addr *pinAddr;

    LPHOSTENT lpHostEnt;

       int                 nRet;

       int                 nLen;

       char        szLocalAddr[80];

       memset( szLocalAddr,0,sizeof(szLocalAddr) );

       // Get our local name

    nRet = gethostname(szLocalAddr,sizeof(szLocalAddr) );

       if (nRet == SOCKET_ERROR)

       {

              return NULL;

       }

       // "Lookup" the local name

       lpHostEnt = gethostbyname(szLocalAddr);

    if (NULL == lpHostEnt)

       {

              return NULL;

       }

    // Format first address in the list

       pinAddr = ((LPIN_ADDR)lpHostEnt->h_addr);

       nLen = strlen(inet_ntoa(*pinAddr));

       if ((DWORD)nLen > sizeof(szLocalAddr))

       {

              WSASetLastError(WSAEINVAL);

              return NULL;

       }

       return inet_ntoa(*pinAddr);

}

int CServer::GetFileList( LPFILE_INF pFI, UINT nArraySize, const char* szPath  )

{

       WIN32_FIND_DATA  wfd;

       int idx = 0;

       CHAR lpFileName[MAX_PATH];

       GetCurrentDirectory( MAX_PATH,lpFileName );

       strcat( lpFileName,"\\" );

       strcat( lpFileName, szPath );

       HANDLE hFile = FindFirstFile( lpFileName, &wfd );

       if ( hFile != INVALID_HANDLE_VALUE )

       {

              pFI[idx].dwFileAttributes = wfd.dwFileAttributes;

              lstrcpy( pFI[idx].szFileName, wfd.cFileName );

              pFI[idx].ftCreationTime = wfd.ftCreationTime;

              pFI[idx].ftLastAccessTime = wfd.ftLastAccessTime;

              pFI[idx].ftLastWriteTime  = wfd.ftLastWriteTime;

              pFI[idx].nFileSizeHigh    = wfd.nFileSizeHigh;

              pFI[idx++].nFileSizeLow   = wfd.nFileSizeLow;

              while( FindNextFile( hFile,&wfd ) && idx < (int)nArraySize )

              {

                     pFI[idx].dwFileAttributes = wfd.dwFileAttributes;

                     lstrcpy( pFI[idx].szFileName, wfd.cFileName );

                     pFI[idx].ftCreationTime = wfd.ftCreationTime;

                     pFI[idx].ftLastAccessTime = wfd.ftLastAccessTime;

                     pFI[idx].ftLastWriteTime  = wfd.ftLastWriteTime;

                     pFI[idx].nFileSizeHigh    = wfd.nFileSizeHigh;

                     pFI[idx++].nFileSizeLow   = wfd.nFileSizeLow;

              }

              FindClose( hFile );

       }

       return idx;

}

char* CServer::HostToNet( char* szPath )

{

       int idx = 0;

       if( NULL == szPath ) return NULL;

       strlwr( szPath );

       while( szPath[idx] )

       {

              if( szPath[idx] == '\\' )

                     szPath[idx] = '/';

              idx ++;

       }

       return szPath;

}

char* CServer::NetToHost( char* szPath )

{

       int idx = 0;

       if( NULL == szPath ) return NULL;

       strlwr( szPath );

       while( szPath[idx] )

       {

              if( '/' == szPath[idx]  )

                     szPath[idx] = '\\';

              idx ++;

       }

       return szPath;

}

char* CServer::RelativeDirectory( char* szDir )

{

       int nStrLen = strlen(m_Directory);

       if( !strnicmp( szDir,m_Directory, nStrLen ) )

              szDir += nStrLen;

       if( szDir && szDir[0] == '\0' ) szDir = "/";

      

       return HostToNet(szDir);

}

char* CServer::AbsoluteDirectory( char* szDir )

{

       char szTemp[MAX_PATH];

       strcpy( szTemp,m_Directory+2 );

       if( NULL == szDir ) return NULL;

       if( '/' == szDir[0] )

              strcat( szTemp, szDir );

       szDir = szTemp ;   

       return NetToHost(szDir);

}