网络程序设计
实验报告
实验名称:___基于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端口号为21Port方式数据端口为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);
}
计算机实用技术实验报告实验题目PTP服务器的搭建班级姓名蒋艳静学号指导教师黄务兰报告日期一实验内容简要描述1实习目标1理解FTP的…
计算机科学与技术系实验报告课程名称计算机网络实验名称ftp服务器的搭建姓名王欢学号124077031045日期成绩教师左红卫一实验…
实验步骤一安装VSFTPD服务器实验前提打开VMwareWorkstation开启FedoraLinux操作系统必须安装了VSFT…
华北电力大学实验报告实验名称课程名称信息安全实验课程学生姓名金祥唐正鑫王鑫专业班级信安1301学号20xx090401072021…
Server-u架构ftp服务器实验报告姓名:XX班级:10级科技二班学号:XXX第一步是提供唯一的域名域名对其用户是不可见的,并…