?? cdownload.cpp
字號:
// cdownload.cpp: implementation of the cdownload class.
//
//////////////////////////////////////////////////////////////////////
//*************************************************************
//作者:趙明
//EMAIL:zmpapaya@hotmail.com;papaya_zm@sina.com
//主頁:http://h2osky.126.com
/********************************************************/
#include "stdafx.h"
#include "client1.h"
#include "cdownload.h"
#include "MainFrm.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#define SERVER_PORT 3962
#define SIZE_OF_zmfile 1080//關于此宏的定義,見server1項目。
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//參數是:“可下載文件列表”List控件中當前列表項的索引。
cdownload::cdownload(int thno1)
{
m_fname="zm.zip";
m_index=-1;
doinfo.totle=0;
doinfo.threadno=thno1;
}
cdownload::~cdownload()
{
}
//在開始傳送之前,向服務器發出“獲得可下載文件列表”的命令,以便讓客戶端知道有哪些文件可下載。
//經過我的搜索,我發現,原來這個函數是個作廢了的東西,根本就沒用到呀?!!!
int cdownload::sendrequest(int n)
{
//獲取服務器信息
sockaddr_in local;
//建套接字
SOCKET m_socket;
int rc=0;
//初使化服務器地址
local.sin_family=AF_INET;
local.sin_port=htons(SERVER_PORT);
local.sin_addr.S_un.S_addr=inet_addr(g_csIP);
//socket函數的第三個參數的默認值是0,表示由程序本身根據地址格式和套接字類型,自動選擇一個合適的協議。
m_socket=socket(AF_INET,SOCK_STREAM,0);
int ret;
//聯接服務器
ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));
//有錯的話
if(ret<0)
{
AfxMessageBox("聯接錯誤");
closesocket(m_socket);
return -1;
}
//初使化命令
fileinfo fileinfo1;
fileinfo1.len=n;
fileinfo1.seek=50;
fileinfo1.type=1;
//發送命令
int aa=sendn(m_socket,(char*)&fileinfo1,100);
if(aa<0)
{
closesocket(m_socket);
return -1;
}
//接收服務器傳來的信息
aa=readn(m_socket,(char*)&fileinfo1,100);
if(aa<0)
{
closesocket(m_socket);
return -1;
}
//關閉
shutdown(m_socket,2);
closesocket(m_socket);
return 1;
}
//下面是真正執行下載文件操作的函數,是本程序中最最核心的東西了!!!
//參數是:cdownload類的m_index成員的值,用來作為filerange和good數組的下標,還用來作為
//輔助文件的文件名后綴的最后一個字符。
UINT cdownload::threadfunc(long index)
{
//初使化連接
sockaddr_in local;
SOCKET m_socket;
int rc=0;
local.sin_family=AF_INET;
local.sin_port=htons(SERVER_PORT);
local.sin_addr.S_un.S_addr=inet_addr(g_csIP);
//socket函數的第三個參數的默認值是0,表示由程序本身根據地址格式和套接字類型,自動選擇
//一個合適的協議。
m_socket=socket(AF_INET,SOCK_STREAM,0);
int ret;
//創建一個“讀入緩沖區”,大小是20
char* m_buf=new char[SIZE];
//remanent中放的是:要下載的這一段文件中,還沒有被下載的字節數,也就是剩余的字節數。
int remanent,len2;
fileinfo fileinfo1;
//連接服務器端。
ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));
//讀入此線程的下載信息。
fileinfo1.seek=filerange[index*2];//在文件中seek的位置。
fileinfo1.len=filerange[index*2+1];//要下載的這一段文件的長度。
remanent=fileinfo1.len;
//發給服務器端的信息中,type=2,表示要求下載文件中的一段。(目前,服務器能識別的type的類型只有0和2這兩種)
fileinfo1.type=2;
//這個字段,大概是“可下載文件列表”中的索引,可以用作對應的數組的下標。
fileinfo1.fileno=doinfo.threadno;
//destination n.目的地(目標,指定)
CFile destFile;//用來保存要下載的文件的數據的文件,是“目標文件”。
FILE* fp=NULL;
//如果打開m_fname文件失敗,說明此文件并不存在,也就是說:這是第一次下載。
if((fp=fopen(m_fname,"r"))==NULL)
//指定了CFile::modeCreate標記,表示一定要創建新文件。
//調試后發現,m_fname中放的就是要下載的文件的真正的文件名。
//注意:文件必須要以CFile::shareDenyNone的方式打開,只有這樣,才能實現多個線程同時
//打開此文件。
destFile.Open(m_fname, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyNone);
//如果此文件已經存在了,則說明這是另一個“正在下載此文件的”線程在運行,或者是續傳。
else
{
//這一句代碼必須要加上,否則,在下載完文件之后,并且客戶端程序沒有退出的情況下,就
//不能刪除或移動或重命名下載的文件,因為fopen函數打開了文件,如果不關閉文件,則文件
//就會被鎖定住。
fclose(fp);//added by yjk
destFile.Open(m_fname,CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
}
//文件指針移到指定位置,是從文件的開始位置開始偏移的。
destFile.Seek(filerange[index*2],CFile::begin);
//發消息給服務器,告訴它“可以傳文件了”。
sendn(m_socket,(char*)&fileinfo1,100);
CFile myfile;//這是一個輔助文件,是以“.down+N”為文件名后綴的。
CString csTemp;
CString temp;
temp.Format(".down%d",index);
//形成了一個輔助下載操作的文件的文件名“XX.down+N”。
csTemp=m_fname+temp;
//打開了“XX.down+N”文件
myfile.Open(csTemp,CFile::modeWrite|CFile::typeBinary|CFile::shareDenyNone);
//當還沒下載完這一段文件的時候,就繼續循環
while(remanent>0)
{
//SIZE宏的大小是20,是緩沖區的大小;
//而remanent中放的是:要下載的這一段文件中,還沒有被下載的字節數。
len2=remanent>SIZE?SIZE:remanent;
//從服務器端讀取len2這么多的數據。
int len1=readn(m_socket,m_buf,len2);
//如果接收數據的時候發生錯誤,則
if(len1==SOCKET_ERROR)
{
closesocket(m_socket);
break;
}
//將剛剛成功下載下來的這一段數據,寫入到“目標文件”中。
destFile.Write(m_buf, len1);
//更改要下載的這一段文件的長度,減去已經下載下來了的部分。
filerange[index*2+1]-=len1;
//前移在文件中seek的位置,也就是把已經下載下來了的那一部分移掉了。
filerange[index*2]+=len1;
//移動“文件指針”到輔助文件的開頭位置。
myfile.Seek(0,CFile::begin);
//將當前的下載情況寫入到輔助文件中,以備以后實現斷點續傳功能。
myfile.Write(&filerange[index*2],sizeof(int));
myfile.Write(&filerange[index*2+1],sizeof(int));
//減去這次循環所讀取的數據的長度。
remanent=remanent-len1;
//totle字段的含義:要被下載的文件段中,已經下載了的字節數。
//對,下載完了一段之后,就需要把新下載的這一段的字節數加上去。
doinfo.totle=doinfo.totle+len1;
};
//要下載的文件的片段下載完成了,做收尾工作。
myfile.Close();//關閉輔助文件。
destFile.Close();//關閉目標文件。
delete [] m_buf;//刪除用來從服務器端接收數據的緩沖區。
shutdown(m_socket,2);//關閉連接socket。
//The shutdown function does not close the socket. Any resources attached to the socket will not be freed until closesocket is invoked.
closesocket(m_socket);//added by yjk
//如果剩余的字節數<=0,則
if(remanent<=0)
good[index]=TRUE;//設為true,大概表示:文件的這一段已經被成功下載下來了。
return 1;
}
//開始下載用戶選中的那個“可下載的文件”。
//參數是:要下載的文件的下標索引。
int cdownload::startask(int n)
{
//讀入文件長度
doinfo.filelen=zmfile[n].length;
//讀入文件名
m_fname=zmfile[n].name;
//給主窗體發消息
CString aaa;
aaa="正在讀取 "+m_fname+" 信息,馬上開始下載。。。\n";
AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
aaa.ReleaseBuffer();
//如果文件長度小于0,則 返回-1
if(doinfo.filelen<=0)
return -1;
//建一個以.down結尾的文件,用來記錄文件信息
CString csTemp;
csTemp=m_fname+".down";
//保存以.down結尾的文件之名。
doinfo.name=csTemp;
FILE* fp=NULL;
CFile myfile;
//Run-Time Library Reference fopen, _wfopen Open a file.
//The character string mode specifies the type of access requested for the file, as follows:
//"r" Opens for reading. If the file does not exist or cannot be found, the fopen call fails.
//Return Value Each of these functions returns a pointer to the open file. A null pointer value indicates an error.
//如果以r的方式打開XX.down文件失敗,則說明此文件不存在,從而說明這是第一次下載此文件,
//于是就:初使化對應于各個下載線程的輔助文件。
if((fp=fopen(csTemp,"r"))==NULL)
{
//added by yjk begin
//看看要下載的文件是否已經存在了,如果是已經存在了,就需要詢問一下用戶,是否要
//重新下載,如果用戶選擇重新下載,將刪除原來的文件。
if((fp=fopen(m_fname,"r"))!=NULL)
{
fclose(fp);
//如果用戶不想重新下載,就返回2,終止這次下載操作。
if(::MessageBox(NULL,"同名的文件已經存在了,如果選擇“是”將覆蓋原來的文件。你是否還要下載此文件?"," YJK 提醒用戶",MB_YESNO|MB_ICONQUESTION)==IDNO)
return 2;
//刪除原來的文件,這樣,就跟以前從沒有下載過此文件一樣了。
DeleteFile(m_fname);
}
//added by yjk end
//這里已經把filerange[0]設定為0了,這表示,數組中的元素的值是從0開始的。
filerange[0]=0;
//文件分塊
//BLOCK的值為4,那么就是從0至3進行循環了。假設文件的長度為203,那么
//“doinfo.filelen/BLOCK”就等于50了。
for(int i=0;i<BLOCK;i++)
{
if(i>0)
//當i==1的時候,filerange[2]=50;
//當i==2的時候,filerange[4]=100;
//當i==3的時候,filerange[6]=150;
// filerange[i*2]=i*(doinfo.filelen/BLOCK+1);//加上1,是為了防止程序在遇到doinfo.filelen/BLOCK==0這種情況的時候,運行出錯。
filerange[i*2]=i*(doinfo.filelen/BLOCK);//added by yjk
//當i==0的時候,filerange[1]=50;
//當i==1的時候,filerange[3]=50;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -