• <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>
            隨筆-250  評論-20  文章-55  trackbacks-0

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


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


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

            一、服務(wù)器端

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

            DWORD WINAPI listenthread(LPVOID lpparam)
            {
            //由主函數(shù)傳來的套接字
              SOCKET  pthis=(SOCKET)lpparam;
            //開始監(jiān)聽
            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;
            }
            //進(jìn)入循環(huán),并接收到來的套接字
            while(1){
            //新建一個套接字,用于客戶端
            SOCKET s1;
            s1=accept(pthis,NULL,NULL);
               //給主函數(shù)發(fā)有人聯(lián)入消息
            CString aa;
            aa="一人聯(lián)入!\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];//文件名
            };
            
            用戶線程函數(shù):
            DWORD WINAPI clientthread(LPVOID lpparam)
            {
            //文件消息
            fileinfo* fiinfo;
            //接收緩存
            char* m_buf;
            m_buf=new char[100];
            //監(jiān)聽函數(shù)傳來的用戶套接字
            SOCKET  pthis=(SOCKET)lpparam;
            //讀傳來的信息
            int aa=readn(pthis,m_buf,100);
            //如果有錯就返回
            if(aa<0){
            closesocket (pthis);
            return -1;
            }
            //把傳來的信息轉(zhuǎn)為定義的文件信息
            fiinfo=(fileinfo*)m_buf;
            CString aaa;
            //檢驗(yàn)客戶想說什么
            switch(fiinfo->type)
            {
            //我要讀文件信息
            case 0:
            //讀文件
            aa=sendn(pthis,(char*)zmfile,1080);
            //有錯
            if(aa<0){
            closesocket (pthis);
            return -1;
            }
            //發(fā)消息給主函數(shù)
            aaa="收到LIST命令\n";
            AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
            break;
            //我準(zhǔn)備好了,可以傳文件了
            case 2:
            //發(fā)文件消息給主函數(shù)
            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="接收協(xié)議錯誤!\n";
            AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
            break;
            }
            return 0;
            }
            讀文件函數(shù)
            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;
            //開始接收,直到發(fā)完整個文件
            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();
            }
            

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

            二、客戶端

            客戶端最重要,也最復(fù)雜,它負(fù)責(zé)線程的管理,進(jìn)度的記錄等工作。

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

            class cdownload
            {
            public:
            void createthread();//開線程
            DWORD finish1();//完成線程
            int sendlist();//發(fā)命令1
            downinfo doinfo;//文件信息(與服務(wù)器定義一樣)
            int startask(int n);開始傳文件n
            long m_index;
            BOOL good[BLACK];
            int  filerange[100];
            CString fname;
            CString fnametwo;
            UINT threadfunc(long index);//下載進(jìn)程
            int sendrequest(int n);//發(fā)文件信息
            cdownload(int thno1);
            virtual ~cdownload();
            };
            下面先介紹sendrequest(int n),在開始前,向服務(wù)器發(fā)獲得文件消息命令,以便讓客戶端知道有哪些文件可傳
            int cdownload::sendrequest(int n)
            {
            //建套接字
            sockaddr_in local;
            SOCKET m_socket;
            int rc=0;
            //初使化服務(wù)器地址
            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;
            //聯(lián)接服務(wù)器
            ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));
            //有錯的話
            if(ret<0){
            AfxMessageBox("聯(lián)接錯誤");
            closesocket(m_socket);
            return -1;
            }
            //初使化命令
            fileinfo fileinfo1;
            fileinfo1.len=n;
            fileinfo1.seek=50;
            fileinfo1.type=1;
            //發(fā)送命令
            int aa=sendn(m_socket,(char*)&fileinfo1,100);
            if(aa<0){
            closesocket(m_socket);
            return -1;
            }
            //接收服務(wù)器傳來的信息
            aa=readn(m_socket,(char*)&fileinfo1,100);
            if(aa<0){
            closesocket(m_socket);
            return -1;
            }
            //關(guān)閉
            shutdown(m_socket,2);
            closesocket(m_socket);
            return 1;
            }
            有了文件消息后我們就可以下載文件了。在主函數(shù)中,用法如下:
            //下載第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);
            //給主函數(shù)發(fā)消息
            CString aaa;
            aaa="正在讀取 "+fname+" 信息,馬上開始下載。。。\n";
            AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
            aaa.ReleaseBuffer();
            //如果文件長度小于0就返回
            if(doinfo.filelen<=0) return -1;
            //建一個以.down結(jié)尾的文件記錄文件信息
            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++){
            //初使化各進(jìn)程記錄文件信息(以.downN結(jié)尾)
            temp.Format(".down%d",ii);
            m_temp=fname+temp;
            myfile.Open(m_temp,CFile::modeCreate|CFile::modeWrite | CFile::typeBinary);
            //寫入各進(jìn)程文件信息
            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{
            //如果文件已存在,說明是續(xù)傳,讀上次信息
            CString temp;
             
            m_temp=fname+".down0";
            if((fp=fopen(m_temp,"r"))==NULL)
            return 1;
            else fclose(fp);
            int bb;
            bb=0;
            //讀各進(jìn)程記錄的信息
            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);
            }
             	//建立下載結(jié)束進(jìn)程timethread,以管現(xiàn)各進(jìn)程結(jié)束時間。
            DWORD dwthread;
            ::CreateThread(NULL,0,timethread,(LPVOID)this,0,&dwthread);
            return 0;
            }
            下面介紹建立各進(jìn)程函數(shù),很簡單:
            void CMainFrame::createthread(int threadno)
            {
            DWORD dwthread;
            //建立BLACK個進(jìn)程
            for(int i=0;i<BLACK;i++)
            {
            m_thread[threadno][i]=	::CreateThread(NULL,0,downthread,(LPVOID)down[threadno],0,&dwthread);
            }
            }
            downthread進(jìn)程函數(shù)
            DWORD WINAPI downthread(LPVOID lpparam)
            {
            cdownload* pthis=(cdownload*)lpparam;
            //進(jìn)程引索+1
            InterlockedIncrement(&pthis->m_index);
            //執(zhí)行下載進(jìn)程
            pthis->threadfunc(pthis->m_index-1);
            return 1;
            }
            
            下面介紹下載進(jìn)程函數(shù),最最核心的東西了
            UINT cdownload::threadfunc(long index)
            {
            //初使化聯(liá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;
            //讀入緩存
            char* m_buf=new char[SIZE];
            int re,len2;
            fileinfo fileinfo1;
            //聯(lián)接
            ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));
            //讀入各進(jìn)程的下載信息
            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
            //如果文件存在,是續(xù)傳
            destFile.Open(fname,CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
            //文件指針移到指定位置
            destFile.Seek(filerange[index*2],CFile::begin);
            //發(fā)消息給服務(wù)器,可以傳文件了
            sendn(m_socket,(char*)&fileinfo1,100);
            CFile myfile;
            CString temp;
            temp.Format(".down%d",index);
            m_temp=fname+temp;
             	//當(dāng)各段長度還不為0時
            while(re>0){
            len2=re>SIZE?SIZE:re;
             
            //讀各段內(nèi)容
            int len1=readn(m_socket,m_buf,len2);
            //有錯的話
            if(len1<0){
            closesocket(m_socket);
            break;
            }
             
            //寫入文件
            destFile.Write(m_buf, len1);
            //更改記錄進(jìn)度信息
            filerange[index*2+1]-=len1;
            filerange[index*2]+=len1;
            //移動記錄文件指針到頭
            myfile.Seek(0,CFile::begin);
            //寫入記錄進(jìn)度
            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;
            }

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

            posted on 2007-04-20 15:18 jay 閱讀(449) 評論(0)  編輯 收藏 引用 所屬分類: socket
            久久亚洲AV无码精品色午夜麻豆| 精品久久久噜噜噜久久久| 亚洲国产精品无码久久98| 一本色道久久88综合日韩精品 | 久久精品国产亚洲av麻豆图片| 亚洲人成网站999久久久综合 | 久久嫩草影院免费看夜色| 亚洲国产成人精品91久久久| 色诱久久久久综合网ywww| 国产精品9999久久久久| 国产69精品久久久久9999| 亚洲精品视频久久久| 久久精品国产亚洲精品2020 | 亚洲va久久久噜噜噜久久男同| 伊人久久大香线焦AV综合影院 | 久久综合香蕉国产蜜臀AV| 久久精品成人免费国产片小草| 亚洲?V乱码久久精品蜜桃| 亚洲va久久久噜噜噜久久天堂 | 一本一道久久综合狠狠老| 午夜不卡888久久| 老色鬼久久亚洲AV综合| 久久久免费观成人影院| 99re久久精品国产首页2020| 伊人久久大香线蕉无码麻豆| 久久国产精品免费一区| 久久久精品人妻一区二区三区四 | 99久久精品国产一区二区| 免费观看成人久久网免费观看| 麻豆久久久9性大片| 久久久久亚洲AV综合波多野结衣 | 无码人妻久久一区二区三区蜜桃| 国产91色综合久久免费分享| 久久精品卫校国产小美女| 国产成人无码精品久久久久免费| 无码专区久久综合久中文字幕| 久久亚洲国产精品五月天婷| 国产精品日韩欧美久久综合| 亚洲精品国产成人99久久| 久久人人爽人人爽人人AV东京热| 色播久久人人爽人人爽人人片aV|