青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

點對點多線程斷點續傳的實現

作者:趙明

下載配套源代碼(網絡傳圣源代碼)
下載地址二 http://h2osky.126.com

在如今的網絡應用中,文件的傳送是重要的功能之一,也是共享的基礎。一些重要的協議像HTTP,FTP等都支持文件的傳送。尤其是FTP,它的全稱就是“文件傳送協議”,當初的工程師設計這一協議就是為了解決網絡間的文件傳送問題,而且以其穩定,高速,簡單而一直保持著很大的生命力。作為一個程序員,使用這些現有的協議傳送文件相當簡單,不過,它們只適用于服務器模式中。這樣,當我們想在點與點之間傳送文件就不適用了或相當麻煩,有一種大刀小用的意味。筆者一直想尋求一種簡單有效,且具備多線程斷點續傳的方法來實現點與點之間的文件傳送問題,經過大量的翻閱資料與測試,終于實現了,現把它共享出來,與大家分享。
我寫了一個以此為基礎的實用程序(網絡傳圣,包含源代碼),可用了基于TCP/IP的電腦上,供大家學習。


(本文源代碼運行效果圖)


實現方法(VC++,基于TCP/IP協議)如下:
仍釆用服務器與客戶模式,需分別對其設計與編程。
服務器端較簡單,主要就是加入待傳文件,監聽客戶,和傳送文件。而那些斷點續傳的功能,以及文件的管理都放在客戶端上。

一、服務器端

首先介紹服務器端:
最開始我們要定義一個簡單的協議,也就是定義一個服務器端與客戶端聽得懂的語言。而為了把問題簡化,我就讓服務器只要聽懂兩句話,一就是客戶說“我要讀文件信息”,二就是“我準備好了,可以傳文件了”。
由于要實現多線程,必須把功能獨立出來,且包裝成線程,首先建一個監聽線程,主要負責接入客戶,并啟動另一個客戶線程。我用VC++實現如下:

DWORD WINAPI listenthread(LPVOID lpparam)
{	

    //由主函數傳來的套接字
  SOCKET  pthis=(SOCKET)lpparam;
    //開始監聽
	int rc=listen(pthis,30);
    //如果錯就顯示信息
    if(rc<0){
	  CString aaa;
	  aaa="listen錯誤\n";
      AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
	  aaa.ReleaseBuffer();
	  return 0;
	}
    //進入循環,并接收到來的套接字
	while(1){
    //新建一個套接字,用于客戶端
	SOCKET s1;
	s1=accept(pthis,NULL,NULL);
	
   //給主函數發有人聯入消息
    CString aa;
    aa="一人聯入!\n";
    AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aa.GetBuffer(0),1);
	aa.ReleaseBuffer();
	DWORD dwthread;
    //建立用戶線程
	::CreateThread(NULL,0,clientthread,(LPVOID)s1,0,&dwthread);	
	}
	return 0;
}
接著我們來看用戶線程:
先看文件消息類定義
struct fileinfo
{
	int fileno;//文件號
	int type;//客戶端想說什么(前面那兩句話,用1,2表示)
	long len;//文件長度
	int seek;//文件開始位置,用于多線程

	char name[100];//文件名
};
用戶線程函數:
DWORD WINAPI clientthread(LPVOID lpparam)
{
	//文件消息
	fileinfo* fiinfo;
	//接收緩存
	char* m_buf;
	m_buf=new char[100];
	//監聽函數傳來的用戶套接字
	SOCKET  pthis=(SOCKET)lpparam;
	//讀傳來的信息
	int aa=readn(pthis,m_buf,100);
	//如果有錯就返回
	if(aa<0){
		closesocket (pthis);
		return -1;
	}
	//把傳來的信息轉為定義的文件信息
	fiinfo=(fileinfo*)m_buf;
	CString aaa;
	//檢驗客戶想說什么
	switch(fiinfo->type)
	{
	//我要讀文件信息
	case 0:
	//讀文件
	aa=sendn(pthis,(char*)zmfile,1080);
	//有錯
	if(aa<0){	
		closesocket (pthis);
		return -1;
	}
	//發消息給主函數
	aaa="收到LIST命令\n";
    	AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
	break;
	//我準備好了,可以傳文件了

	case 2:
	//發文件消息給主函數
	aaa.Format("%s  文件被請求!%s\n",zmfile[fiinfo->fileno].name,nameph[fiinfo->fileno]);
	AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
	//讀文件,并傳送
	readfile(pthis,fiinfo->seek,fiinfo->len,fiinfo->fileno);
	//聽不懂你說什么

	default:
	aaa="接收協議錯誤!\n";
    	AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
	break;
}

	return 0;
}
讀文件函數
void readfile(SOCKET  so,int seek,int len,int fino)
{
	//文件名
	CString myname;
	myname.Format("%s",nameph[fino]);
	CFile myFile;
	//打開文件
	myFile.Open(myname, CFile::modeRead | CFile::typeBinary|CFile::shareDenyNone); 
	//傳到指定位置 
	myFile.Seek(seek,CFile::begin);
	char m_buf[SIZE];
	int len2;
	int len1;
	len1=len;
	//開始接收,直到發完整個文件
	while(len1>0){
		len2=len>SIZE?SIZE:len;
		myFile.Read(m_buf, len2);
		int aa=sendn(so,m_buf,len2);
	if(aa<0){	
		closesocket (so);
		break;
	}
	len1=len1-aa;
	len=len-aa;
	}
	myFile.Close();
}

服務器端最要的功能各技術就是這些,下面介紹客戶端。

二、客戶端

客戶端最重要,也最復雜,它負責線程的管理,進度的記錄等工作。

大概流程如下:
先連接服務器,接著發送命令1(給我文件信息),其中包括文件長度,名字等,然后根據長度決定分幾個線程下載,并初使化下載進程,接著發送命令2(可以給我傳文件了),并記錄文件進程。最后,收尾。
這其中有一個十分重要的類,就是cdownload類,定義如下:

class cdownload  
{
public:
	void createthread();//開線程
	DWORD finish1();//完成線程
	int sendlist();//發命令1
	downinfo doinfo;//文件信息(與服務器定義一樣)
	int startask(int n);開始傳文件n
	long m_index;
	BOOL good[BLACK];
	int  filerange[100];
	CString fname;
	CString fnametwo;
	UINT threadfunc(long index);//下載進程

	int sendrequest(int n);//發文件信息
	cdownload(int thno1);
	virtual ~cdownload();
};
下面先介紹sendrequest(int n),在開始前,向服務器發獲得文件消息命令,以便讓客戶端知道有哪些文件可傳
int cdownload::sendrequest(int n)
{
	//建套接字
	sockaddr_in local;
	SOCKET m_socket;

	int rc=0;
	//初使化服務器地址
	local.sin_family=AF_INET;
	local.sin_port=htons(1028);
	local.sin_addr.S_un.S_addr=inet_addr(ip);
	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;
}
有了文件消息后我們就可以下載文件了。在主函數中,用法如下:
//下載第clno個文件,并為它建一個新cdownload類
down[clno]=new cdownload(clno);
//開始下載,并初使化
type=down[clno]->startask(clno);
//建立各線程
createthread(clno);
下面介紹開始方法:
//開始方法
int cdownload::startask(int n)
{
	//讀入文件長度
	doinfo.filelen=zmfile[n].length;
	//讀入名字
	fname=zmfile[n].name;
	CString tmep;
	//初使化文件名
	tmep.Format("\\temp\\%s",fname);

	//給主函數發消息
	CString aaa;
	aaa="正在讀取 "+fname+" 信息,馬上開始下載。。。\n";
	AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
	aaa.ReleaseBuffer();
	//如果文件長度小于0就返回
	if(doinfo.filelen<=0) return -1;
	//建一個以.down結尾的文件記錄文件信息
	CString m_temp;
	m_temp=fname+".down";
	
	doinfo.name=m_temp;
	FILE* fp=NULL;
	CFile myfile;
	//如果是第一次下載文件,初使化各記錄文件

	if((fp=fopen(m_temp,"r"))==NULL){
	filerange[0]=0;
	//文件分塊
	for(int i=0;i<BLACK;i++)
	{
		if(i>0)
			filerange[i*2]=i*(doinfo.filelen/BLACK+1);
		filerange[i*2+1]=doinfo.filelen/BLACK+1;
	}
	filerange[BLACK*2-1]=doinfo.filelen-filerange[BLACK*2-2];

	myfile.Open(m_temp,CFile::modeCreate|CFile::modeWrite | CFile::typeBinary);

	//寫入文件長度
	myfile.Write(&doinfo.filelen,sizeof(int));
	myfile.Close();
 
	CString temp;
	for(int ii=0;ii<BLACK;ii++){
	//初使化各進程記錄文件信息(以.downN結尾)

	temp.Format(".down%d",ii);
	m_temp=fname+temp;
	myfile.Open(m_temp,CFile::modeCreate|CFile::modeWrite | CFile::typeBinary);
	//寫入各進程文件信息
	myfile.Write(&filerange[ii*2],sizeof(int));
	myfile.Write(&filerange[ii*2+1],sizeof(int));
	myfile.Close();
	}

	((CMainFrame*)::AfxGetMainWnd())->m_work.m_ListCtrl->AddItemtwo(n,2,0,0,0,doinfo.threadno);
	}
	else{
	//如果文件已存在,說明是續傳,讀上次信息
	CString temp;
 
	m_temp=fname+".down0";
	if((fp=fopen(m_temp,"r"))==NULL)
		return 1;
	else fclose(fp);

	int bb;
	bb=0;
	//讀各進程記錄的信息
	for(int ii=0;ii<BLACK;ii++)
	{
		temp.Format(".down%d",ii);
		m_temp=fname+temp;
 
		myfile.Open(m_temp,CFile::modeRead | CFile::typeBinary);
		myfile.Read(&filerange[ii*2],sizeof(int));
		myfile.Read(&filerange[ii*2+1],sizeof(int));
		myfile.Close();

		bb = bb+filerange[ii*2+1];
		CString temp;
	}
	if(bb==0) return 1;
	doinfo.totle=doinfo.filelen-bb;
 
	((CMainFrame*)::AfxGetMainWnd())->m_work.m_ListCtrl->AddItemtwo(n,2,doinfo.totle,1,0,doinfo.threadno);

	}

 	//建立下載結束進程timethread,以管現各進程結束時間。
	DWORD dwthread;
	::CreateThread(NULL,0,timethread,(LPVOID)this,0,&dwthread);

	return 0;
}
下面介紹建立各進程函數,很簡單:
void CMainFrame::createthread(int threadno)
{
	DWORD dwthread;
	//建立BLACK個進程
	for(int i=0;i<BLACK;i++)
	{
		m_thread[threadno][i]=	::CreateThread(NULL,0,downthread,(LPVOID)down[threadno],0,&dwthread);
	}
}
downthread進程函數
DWORD WINAPI downthread(LPVOID lpparam)
{
	cdownload* pthis=(cdownload*)lpparam;
	//進程引索+1
	InterlockedIncrement(&pthis->m_index);
	//執行下載進程
	pthis->threadfunc(pthis->m_index-1);
	return 1;
}
下面介紹下載進程函數,最最核心的東西了
UINT cdownload::threadfunc(long index)
{
	//初使化聯接
	sockaddr_in local;
	SOCKET m_socket;

	int rc=0;
 
	local.sin_family=AF_INET;
	local.sin_port=htons(1028);
	local.sin_addr.S_un.S_addr=inet_addr(ip);
	m_socket=socket(AF_INET,SOCK_STREAM,0);

	int ret;
	//讀入緩存
	char* m_buf=new char[SIZE];
	int re,len2;
	fileinfo fileinfo1;
	//聯接
	ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));
	//讀入各進程的下載信息
	fileinfo1.len=filerange[index*2+1];
	fileinfo1.seek=filerange[index*2];
	fileinfo1.type=2;
	fileinfo1.fileno=doinfo.threadno;
 
	re=fileinfo1.len;
 
	//打開文件 
	CFile destFile;
	FILE* fp=NULL;
	//是第一次傳的話
	if((fp=fopen(fname,"r"))==NULL)
		destFile.Open(fname, CFile::modeCreate|CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
	else
		//如果文件存在,是續傳
		destFile.Open(fname,CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
	//文件指針移到指定位置
	destFile.Seek(filerange[index*2],CFile::begin);
	//發消息給服務器,可以傳文件了
	sendn(m_socket,(char*)&fileinfo1,100);

	CFile myfile;
	CString temp;
	temp.Format(".down%d",index);
	m_temp=fname+temp;

 	//當各段長度還不為0時
	while(re>0){
		len2=re>SIZE?SIZE:re;
 
		//讀各段內容
		int len1=readn(m_socket,m_buf,len2);
		//有錯的話
		if(len1<0){
			closesocket(m_socket);
			break;
		}
 
	//寫入文件
	destFile.Write(m_buf, len1);	

	//更改記錄進度信息

	filerange[index*2+1]-=len1;
	filerange[index*2]+=len1;
	//移動記錄文件指針到頭
	myfile.Seek(0,CFile::begin);
	//寫入記錄進度
	myfile.Write(&filerange[index*2],sizeof(int));
	myfile.Write(&filerange[index*2+1],sizeof(int));

	//減去這次讀的長度
	re=re-len1;

	//加文件長度
	doinfo.totle=doinfo.totle+len1;
	};
	
	//這塊下載完成,收尾
 
	myfile.Close();
	destFile.Close();
	delete [] m_buf;
	shutdown(m_socket,2);
 
 
	if(re<=0) good[index]=TRUE;
	return 1;
}

到這客戶端的主要模塊和機制已基本介紹完。希望好好體會一下這種多線程斷點續傳的方法。

作者信息:
姓名:趙明
email: papaya_zm@sina.com 或 zmpapaya@hotmail.com
主頁: http://h2osky.126.com

posted on 2006-06-20 18:56 楊粼波 閱讀(370) 評論(0)  編輯 收藏 引用 所屬分類: 文章收藏

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲综合激情| 亚洲剧情一区二区| 欧美一区二区三区另类| 欧美一区二区私人影院日本| 亚洲视频一区二区| 久久久久国产成人精品亚洲午夜| 久久久久高清| 亚洲精品一级| 欧美在线三区| 亚洲欧洲一区二区三区在线观看 | 国产情人节一区| 一区在线视频| 一区二区三区久久久| 久久精品成人一区二区三区| 91久久精品国产91久久性色| 欧美色图五月天| 在线日韩欧美| 久久精品亚洲一区二区三区浴池 | 中文一区二区| 最新日韩在线视频| 国产精品一区在线观看| 亚洲素人在线| 欧美在线不卡视频| 国产精品丝袜久久久久久app| 亚洲欧洲视频在线| 久久综合久久久久88| 亚洲欧美综合另类中字| 国产精品福利网| 亚洲色图自拍| 久久久久久久国产| 亚洲在线观看视频网站| 亚洲人成网站精品片在线观看| 久久久夜精品| 亚洲欧美日本日韩| 一区二区三区日韩在线观看 | 国产欧美日韩免费看aⅴ视频| 欧美成人精品h版在线观看| 午夜国产精品影院在线观看| 亚洲精选中文字幕| 亚洲国产精品第一区二区三区| 麻豆精品视频在线| 国内成人精品视频| 久久精品综合| 国产精品区二区三区日本| 欧美激情精品久久久久久黑人 | 亚洲国产精选| 激情久久久久久| 性欧美18~19sex高清播放| 国产一在线精品一区在线观看| 日韩亚洲欧美中文三级| 国产精品五月天| 一区二区三区视频观看| 夜夜嗨av一区二区三区免费区| 亚洲精品在线视频观看| 亚洲娇小video精品| 亚洲精品日韩欧美| 亚洲欧洲精品一区二区| 久久亚洲春色中文字幕| 夜夜爽99久久国产综合精品女不卡 | 亚洲第一综合天堂另类专| 欧美一区二区视频97| 亚洲高清av在线| 制服丝袜亚洲播放| 亚洲欧美日韩国产另类专区| 国产精品久久97| 亚洲视频大全| 午夜精品一区二区三区在线| 久久久美女艺术照精彩视频福利播放| 久久er精品视频| 国产一区二区三区av电影| 亚洲黄色片网站| 99日韩精品| 欧美日韩国产综合视频在线观看| 性欧美超级视频| 久久亚洲国产精品一区二区| 欧美承认网站| 国产精品免费久久久久久| 欧美激情在线狂野欧美精品| 91久久精品美女高潮| 欧美精品一区二区三区高清aⅴ| 亚洲综合成人婷婷小说| 国产乱人伦精品一区二区| 羞羞答答国产精品www一本 | 一本久道久久综合狠狠爱| 欧美日韩国产成人在线观看| 宅男精品视频| 久久久久在线观看| 国产精品美女主播| 久久久久久色| 99亚洲精品| 日韩视频一区二区三区在线播放免费观看 | 久久夜色精品国产| 日韩视频一区二区三区在线播放| 欧美日韩在线播放一区| 亚洲欧洲日本国产| 欧美视频在线一区二区三区| 欧美亚洲网站| 亚洲精品影视| 蜜臀久久99精品久久久久久9 | 狂野欧美一区| 久久综合中文字幕| 国产一区二区三区黄视频| 美女999久久久精品视频| 亚洲视频在线观看免费| 亚洲天堂成人在线观看| 激情亚洲网站| 欧美性淫爽ww久久久久无| 亚洲日本中文字幕区| 欧美在线看片| 亚洲视频专区在线| 亚洲人体1000| 在线不卡视频| 久久性天堂网| 午夜在线电影亚洲一区| 日韩午夜视频在线观看| 欧美 亚欧 日韩视频在线| 欧美一激情一区二区三区| 日韩亚洲视频| 欧美视频一区二区三区在线观看 | 一本色道久久88精品综合| 男女精品网站| 久久久亚洲人| 欧美在线|欧美| 亚洲一区二区三区777| 国产精品v欧美精品v日韩 | 亚洲男人的天堂在线| 亚洲精品一区二区三区蜜桃久 | 亚洲欧美日韩精品| 亚洲一区日本| 亚洲欧美一区二区三区极速播放 | 国产一区二区三区四区hd| 国产精品毛片大码女人| 欧美日韩综合| 欧美视频精品在线| 欧美日韩国产在线播放网站| 欧美国内亚洲| 99国产精品久久久久久久| 亚洲日本成人网| 亚洲黄色视屏| 亚洲国产精品一区二区三区| 亚洲第一页自拍| 亚洲免费视频中文字幕| 亚洲天堂男人| 午夜亚洲福利| 久久国产日韩欧美| 久久精品国产亚洲a| 久久蜜臀精品av| 欧美1区2区| 亚洲精品免费在线播放| 亚洲日本成人| 亚洲视频在线观看网站| 午夜一区二区三区不卡视频| 久久国产免费看| 亚洲色图在线视频| 亚洲影音一区| 亚洲人成77777在线观看网| 亚洲精品午夜精品| 亚洲永久免费av| 久久久999精品| 亚洲自拍偷拍麻豆| 久久av一区二区三区漫画| 久久免费国产精品| 欧美女主播在线| 国产日韩欧美一区二区三区在线观看 | 亚洲精品一级| 亚洲欧美日韩一区二区在线 | 亚洲经典一区| 亚洲欧美日韩国产精品| 久久一区二区三区超碰国产精品| 亚洲二区视频| 亚洲一区二区三区四区五区午夜| 欧美一区二区三区四区高清| 久久综合一区二区| 国产精品av一区二区| 精品av久久707| 亚洲夜间福利| 欧美国产大片| 亚洲影院免费观看| 欧美风情在线| 国模精品一区二区三区色天香| 亚洲精品久久久久中文字幕欢迎你 | 国产精品久久久久av免费| 亚洲高清久久久| 亚洲在线观看| 亚洲国产精品悠悠久久琪琪 | 亚洲最新合集| 噜噜噜躁狠狠躁狠狠精品视频| 欧美午夜一区二区福利视频| 影音先锋久久精品| 午夜精品久久久久久久| 欧美激情久久久久| 亚洲一区欧美激情| 欧美精品www在线观看| 欧美岛国激情| 欧美成年人网站| 国内精品久久久| 香蕉成人伊视频在线观看| 亚洲国产日韩在线| 久久麻豆一区二区| 国产日本亚洲高清|