c++ 网络文件传输_第1页
c++ 网络文件传输_第2页
c++ 网络文件传输_第3页
c++ 网络文件传输_第4页
c++ 网络文件传输_第5页
已阅读5页,还剩19页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、建议读者再看实验步骤之前,先阅读一下文章末尾的【注意事项】一节。这将有助于更好的理解本章的实现。一单线程文件传输 (I):·服务器端(负责发送数据)的实现1 建立一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。2 在对话框上添加“发送”按钮。3 为“发送”按钮添加事件BN_CLICKED的响应函数OnSend()。void CServerDlg:OnSend() / TODO: Add your control notification handler code hereCFileDialog fd(TRUE); / CFileDialog是M

2、FC提供的一个用于选择文件的对话框类CString filename;char fn40;CSocket listenSocket, socketSend;CFile file;long FileLength;char* data;if(IDOK=fd.DoModal() / 启动用于选择文件的对话框/选择了文件filename=fd.GetFileName(); / 获取用户选择的文件的文件名if(!file.Open(filename.GetBuffer(0),CFile:modeRead| File:typeBinary)AfxMessageBox(" 打开文件错误,取消发送!

3、");return;strcpy(fn,filename.GetBuffer(0);else return; /按了取消按钮listenSocket.Create(7000,SOCK_STREAM); listenSocket.Listen(5); listenSocket.Accept(socketSend); FileLength = file.GetLength(); / 获取文件的长度socketSend.Send(&FileLength, 4); / 把要发送的文件的长度传送给对方socketSend.Send(fn,40); / 发送要传送的文件的文件名data

4、= new charFileLength; /分配一块和要传输的文件一样大小的内存空间file.ReadHuge(data, FileLength); /把文件中所有的数据一次性读入datasocketSend.Send(data, FileLength); /把data中的数据都发送出去file.Close();delete data;socketSend.Close();·客户端(负责接收数据)的实现1 建立一个基于对话框的工程,并在建立的过程中选择支持windows socket。(为了能够利用Server端的代码,在程序编写时,可以复制Server的代码到Client目录,并

5、在Server的基础上修改或添加代码)2 在对话框上添加“接收”按钮。3 为“发送”按钮添加事件BN_CLICKED的响应函数OnReceive ()。void CServerDlg:OnReceive() / TODO: Add your control notification handler code hereCSocket socketReceive;CFile file;long FileLength;char * data;char fn40;socketReceive.Create();socketReceive.Connect("127.0.0.1", 70

6、00); /这里为了测试的方便,我们使用127.0.0.1本地回路IPsocketReceive.Receive(&FileLength, 4); /获取要接受的文件的长度socketReceive.Receive(fn, 40); /获取要接受的文件的文件名data = new charFileLength; socketReceive.Receive(data, FileLength); /获取要接受的文件内容file.Open(fn,CFile:modeCreate|CFile:modeWrite | CFile:typeBinary); /在当前目录建立文件file.Write

7、Huge(data, FileLength); file.Close();delete data;socketReceive.Close();AfxMessageBox("接收文件成功");上面的程序以最简单的方式实现了文件传输功能。但正是因为它的简单,所以忽略了很多重要的东西。读者读到这里的时候可以考虑一下可能存在着些什么问题。在这里给出一些上面程序的不足:1 在本书的原理部分曾经提到阻塞式和非阻塞式两种套接字的工作方式。在上面的程序中使用的CSocket类提供了阻塞式的服务,这令编写程序非常方便。然而这却不利于程序的友好性和可用性服务器端在没有获得客户端连接的时候固执的

8、侦听,一点也不理会用户的命令。对话框不再响应消息,用户只能用强制关闭来结束程序。2 希望一次性地动态分配整个文件大小的堆存贮区作为读入数据的内存空间。这个空间一方面受到堆大小的限制,另一方面也受到CFile类成员函数ReadHuge()和WriteHuge()的读写能力限制,还有Receive()和Send()函数一次能够发送的数据也以其参数的最大值为限,所以在遇到大中型文件的时候,系统将不能满足程序提出的动态分配大块内存的要求,这样传输便不得不终止。3 在实际的网络传输中,网络情况是十分多变和复杂的。通过CSocket的成员函数们的返回值可以了解传输的状态,然而在上面的程序中却没有使用这些异

9、常控制,带来的直接后果就是当遇到网络拥塞或对方传送的数据暂时未到时,程序就会认为传输结束而出乎意料的终止。4 在上面的程序中,程序没有传送文件的属性数据,而只通过套接字传送文件数据。在实际应用中这种假设通常是不存在的,所以应当把文件属性也传给对方,以达到文件的完全复制。改进方法:1 使用CAsyncSocket实现异步套接字,避免阻塞2 化整为零,把文件分若干次读入并发送3 在程序中加入异常控制语句已应付网络多变的情况4 在传送文件数据之前获取文件属性值传给对方下面将给出基于上述改进方案的前三点而重新编写的程序,对于第四点,有兴趣的读者可以自己实现。二单线程文件传输(II):·服务器

10、端(负责发送数据)的实现1 建立一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。2 在对话框上添加“侦听”,“发送”按钮。3 用Class Wizard添加一个派生于CAsyncSocket的类CMySocket。4 添加CMySocket类的全局变量listenSocket用于侦听。添加CMySocket类全局变量的位置,最宜在MySocket.cpp的头部。如下:/ MySocket.cpp : implementation file/#include "stdafx.h"#include "Server.h"

11、#include "MySocket.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif/ CMySocketCMySocket listenSocket;CMySocket:CMySocket()CMySocket:CMySocket()本章中以后所有的CMySocket型全局变量都应仿效本段程序添加。5 添加CMySocket类的全局变量sendSocket,用于传输数据6 为“发送”按钮添加事件BN_CLICKED的响应函数OnSend()d

12、efine ReadSize 500void CServerDlg :OnSend()/ TODO: Add your control notification handler code hereCFile file;char dataReadSize; / 用于存放读入的文件数据块long ByteSended=0, FileLength,count;CFileDialog fd(TRUE);CString filename;char fn40;if(IDOK=fd.DoModal() / 启动用于选择文件的对话框/选择了文件filename=fd.GetFileName(); /获取选择的

13、文件的文件名if(!file.Open(filename.GetBuffer(0),CFile:modeRead|CFile:typeBinary)AfxMessageBox("打开文件错误,取消发送!");return;strcpy(fn,filename.GetBuffer(0);else return; /按了取消按钮FileLength=file.GetLength();sendSocket.Send(&FileLength,sizeof(long);sendSocket.Send(fn,40);memset(data,0,sizeof(data);do/

14、从文件读取数据,每次最多读入ReadSize个字节。count表示实际读入的字节数count=file.ReadHuge(data, ReadSize);/ 发送数据while(SOCKET_ERROR=sendSocket.Send(data,count)/ 统计已发送的字节数ByteSended =ByteSended+count;while(ByteSended <FileLength); file.Close();7 为“侦听”按钮添加事件BN_CLICKED的响应函数OnListen ()void CServerDlg:Listen()/ TODO: Add your cont

15、rol notification handler code herelistenSocket.Create(7000);listenSocket.Listen(); 8 为CMySocket类添加消息OnAccpet响应函数OnAccept()void CMySocket:OnAccept(int nErrorCode) / TODO: Add your specialized code here and/or call the base classif(!listenSocket.Accept(sendSocket)AfxMessageBox("连接失败");return

16、;AfxMessageBox("连接成功");CAsyncSocket:OnAccept(nErrorCode); ·客户端(负责接收数据)的实现1 建立一个基于对话框的工程,并在建立的过程中选择支持windows socket。2 添加CMySocket类的全局变量receiveSocket,用于接收数据3 在对话框上添加“连接”和“接收”按钮。4 为“接收”按钮添加事件BN_CLICKED的响应函数OnReceive()define ReadSize 500void CServerDlg:OnReceive()/ TODO: Add your control

17、notification handler code hereCFile file;char dataReadSize;long FileLength;long WriteOnce;long WriteCount = 0;char fn40;/ 接收文件长度while(SOCKET_ERROR =receiveSocket. Receive(&FileLength, sizeof(long)/ 接收文件名while(SOCKET_ERROR =receiveSocket. Receive(fn, 40) if(!file.Open(fn,CFile:modeCreate|CFile:mo

18、deWrite)AfxMessageBox("Create file error!");return;doWriteOnce =receiveSocket.Receive(data,ReadSize);if(WriteOnce =SOCKET_ERROR)continue;/ 统计已接受的字节数WriteCount=WriteCount+WriteOnce;/ 把收到的数据写入文件file.WriteHuge(data,WriteOnce);while(WriteCount< FileLength);file.Close();AfxMessageBox("接

19、收文件成功");5 为“连接”按钮添加事件BN_CLICKED的响应函数OnConnecting()void CServerDlg:OnConnecting() / TODO: Add your control notification handler code herereceiveSocket.Create();receiveSocket.Connect("127.0.0.1",7000);看过了上面的程序后,是不是有个疑问为什么在发送端发送了固定数量的数据(ReadSize个字符),而在接受方WriteOnce =receiveSocket.Receive(

20、data,ReadSize);语句后的已接收字符统计时并没有直接加ReadSize,而是加上了Receive()函数的返回值WriteOnce?请看下面的一个小实验:发送方一次发送10000字节的数据,而在接受方用下面的代码接受数据:CString str;int received=0, total=0;char data10000;while(total<10000)while(SOCKET_ERROR=(received= receiveSocket.Receive(data,10000)str.Format(“received=%d”,received);total=total+r

21、eceived;AfxMessageBox(str);str.Format(“total=%d”,total);AfxMessageBox(str);每次运行实验程序后,变量total的值是不变的,它在两重循环都结束后总保持值为10000。但每次实验弹出的对话框显示的received的值总是变化的。一种可能的情况是,三次弹出显示received值的对话框,每次的显示依次为received =1987 , received= 4012 , received= 4001。这说明我们的SOCK_STREAM型的套接字总能保证数据的完整传输,但它并不能保证数据一次性的到达。实际上,在网络参考模型的结构

22、中socket处于传输层。在传输层发送的数据需要经过底下多个层次的分解和打包,最后才达到的物理层(过程中的分段和打包的操作对于程序员是不可见的)。socket连接基于TCP协议,而TCP连接是字节流而非报文流(报文的边界并不按头尾衔接的方式保存)。例如,如果发送进程将4块512字节的数据写到TCP流上,那么这些数据可能按4个512字节的顺序或2个1024的顺序或者1个2048字节的数据块,或者是其他一些方式传送到接收进程的。接收方无法测出这些数据是以哪种单位写入的。用Windows socket创建的每个套接字都有一块属于自己的缓冲区(Windows下默认的socket发送和接收缓冲区大小均是

23、4096字节,要想改变或获取它的大小可使用函数setsockopt()和getsockopt()<详见MSDN>)。控制socket缓冲区的大小可以优化网络程序的传输性能,但同时也为我们带来了一个问题:当一个应用程序把数据送给TCP实体时,TCP根据自己的判断,可能会立刻将其发送出去或将其缓存起来(为了收集较大量的数据,然后发送)。然而,有时候应用程序很想将数据立即发送出去。例如,假设一个用户登录到了远端的机器上,他输入一条命令并按下回车键之后,该命令行应该立刻送往远端机器而不是暂存起来直到用户输入了下一条命令后再发送。为了强制立即发送数据,应用程序可以使用PUSH标志,通知TCP

24、不能耽搁数据的发送。一些早期的应用程序使用PUSH标志作为一种记号来区分出报文的边界。这种方法有时可以奏效,但有时也会失败,因为并非所有的TCP实现都将PUSH标志传送到接收方的应用程序。而且,如果在每个PUSH标志发送前又有PUSH标志输入进来(例如由于输出线路很忙),那么TCP将会随意地将所有带PUSH标志的数据聚集成一个IP数据报。在这些各种各样的数据片之间不再加以区分(其实,UNIX系统中的文件也有这种特性。读取文件时无法分清文件是一次写一个块、一个字节还是一次全部写入。如同对待UNIX文件一样,TCP软件不知道字节表示什么,并且不无疑去弄清楚,字节就是字节。) Andrew S.Ta

25、nenbaum 著计算机网络(第三版)P403为了解决这个问题,有时不得不编写程序,统计收到的字符数,然后,根据再已知的数据段的长度来区分每个数据段。下面,让来看一看如何在客户端区分已知长度的数据段。(假设已知数据段的长度是2,并且以作为数据发送结束的标志)服务器端:char data100="0123456789#"CSocket socketListen, socketSend;socketListen.Create(7000);socketListen.Listen();socketListen.Accept(socketSend);socketSend.Send(d

26、ata,strlen(data); / 服务器端一次把所有的数据发送出去socketListen.Close();socketSend.Close();客户端:char buffer1; / 用以储存每次收到的一个字符char temp100; / 用以储存正在合并中的一个数据段int i=0;CSocket socketReceive;socketReceive.Create();socketReceive.Connect("127.0.0.1",7000);memset(temp,0,sizeof(temp);dowhile(SOCKET_ERROR=socketRec

27、eive.Receive(buffer,1); / 一次接收一个字符tempi+=buffer0; / 把接收到的字符加入正在形成的数据段的尾部if(i=2) / 如果收到的字符数等于已知数据段的长CString str;str.Format("收到两个字节 %s",temp);AfxMessageBox(str); / 显示这个数据段memset(temp,0,sizeof(temp); / 清空这个数据段,为接收下一个数据段作准备i=0;while(buffer0!='#'); / 判断接收到的是否是结束标志socketReceive.Close();三

28、多线程文件传输如果仔细地统计单线程传输文件的速度,便会发现即使在像湖南大学这种高速的校园网内,传输的速率也不会超过400KB/S。然而,在用下载工具(比如:netants,flashfxp等)下载资料时,时常可以看到大于600KB/S的速度。疑问由此产生难道这些软件采用了“五鬼搬运术”?其实,当探究过其中的原理之后,便会惊奇的发现原来这些软件真的使用了具有神话色彩的“五鬼搬运术”,而驱使这些“鬼”来搬运数据的正是多线程技术!多线程技术在网络程序设计方面有着很大的作用,它不仅能提高程序的发送能力,而且极大地方便了程序员设计编写文件服务器程序(同时处理多个连接)。但是,多线程程序设计有一个很大的问

29、题就是要涉及到线程的同步操作,这确实是一件令人头痛的事情,但为了实现高性能的传输程序,也不得不把它弄个清楚(后文将介绍一些线程技术,至少在文件传输方面会涉及到的一些知识)。在MFC中,启动线程有多种方法:AfxBeginThread(),CreateThread(),还有就是process.h中定义的_beginthread()。传输文件时,不仅应使用多个线程传输文件数据,另外,最好启动一个线程监视文件传输的状态(传输进度等),以便作一些必要的操作,比如:接收方在接收完成时,合并每个线程接收的数据文件;又如:即时显示传输进度或计算传输速率。操作系统课程讲述了线程PV同步原语,锁和条件变量。在M

30、FC中,用CEvent等类来控制线程同步。CEvent和条件变量比较像,它的成员函数SetEvent()用来把这个事件对象设置为有信号的,ResetEvent()函数用来把这个事件对象设置为无信号的,可配套使用的函数WaitForSingleObject用来挂起线程直到事件对象从无信号的变为有信号的。向线程传递数据可以通过下面多种方式实现,以下的两种方式相对简便:1. 以函数值参数方式,在创建线程时传入;这种方式十分可靠,因为每个函数值参数(都会在函数体内保存一个副本,不会受其他线程修改变量的影响),但是只能在创建线程的时候传入,缺乏灵活性;2. 使用全局变量。使用全局变量要用CMutex类的

31、锁机制保护好,否则会导致线程读入数据的错误。 下面的多线程程序将使用第一种方式为每个线程“工人”分配工作编号(idx)。在发送端程序中,多个线程同时读一个欲发送的文件。特别需要注意的是,如果不在打开文件时作一些特别的设置,虽然第一个程序可以正常的打开文件,但之后的程序将因为第一个程序对文件的“排他读”行为而打开文件失败。因此所有的线程打开文件时都应该设置共享读写属性 CFile:shareDenyNone。设置方法如下:CFile file;File.Open(“abc.txt”,CFile:modeRead|CFile:shareDenyNone);上文曾把线程比作工人,它们把数据从一台计算

32、机搬到另一台计算机。为了使它们能完成这项任务,必须教会它们同样的搬运技术,也就是说,需要为它们编写几乎相同的工作代码。然而,毕竟程序中的每个线程要负责一个文件中不同的数据段,所以它们还是有一些小小的区别,比如:它们必须知道各自负责搬动的物品放在哪里(源文件的哪一段)和搬到哪里去(存放在接受方的什么文件中)。另外,要管理好这些工人并为它们分配工作也是一件重要的任务,所以派一个工头(也是一个线程)来完成这项任务是必不可少的不同的,但与现实不同的是,下面程序中的每个工头手下只有一个工人。使用n个线程传输文件时,应当把文件分为大小类似的n块,每个线程负责其中的一块。在客户端,让每个接收线程(工人)把它

33、们收到的数据保存为一个独立的文件,当所有的线程都工作结束时,再把这些文件合并起来。·服务器端(负责发送数据)的实现1 一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。2 在对话框上添加“侦听”和“发送”按钮。3 用ClassWizard添加一个派生于CAsyncSocket的类CMySocket4 定义CMySocket型的全局变量listenSocket和数组sendSockets5 /listenSocket用于侦听,sendSockets中的5个套接字分别被五个线程用来收发数据下面的程序总共启动5个线程用来传输文件数据。每个线程根据自己的

34、编号idx使用套接字数组sendSocket5中相应的套接字传送数据。注意,在Create这个套接字数组的时候,完全可以让它们共享一个端口号。使用CAsyncSocket类时,我们无需做更多的设置就可以使用这项技术。5 双击“侦听”按钮,为它添加事件BN_CLICKED的响应函数 OnListen()void CServerDlg :OnListen()/ TODO: Add your control notification handler code herelistenSocket.Create(7000);listenSocket.Listen();6 为CMySocket类添加消息On

35、Accpect的响应函数OnAccept()void CMySocket:OnAccept(int nErrorCode) / TODO: Add your specialized code here and/or call the base classstatic int File_Socket_Accepted=0;if(!listenSocket.Accept(sendSocketsFile_Socket_Accepted) / 接收对方套接字的连接AfxMessageBox("接收连接失败!");return;sendSocketsFile_Socket_Accep

36、ted.AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE);File_Socket_Accepted+; / 已接收的连接数加一if(File_Socket_Accepted=5) / 如果所有的连接已经都接受了AfxMessageBox("连接成功");listenSocket.Close();CAsyncSocket:OnAccept(nErrorCode);7 编写为线程分配要传输的数据块的全局函数GetBeginPos()/获取线程负责的文件数据块起始位置及大小,TotalThreads为启动传输线程的总数,ThreadIndex为线程的编

37、号,BgPos为计算得到的数据块相对于文件头的偏移量,BlkSize为数据块的大小void GetBeginPos(int TotalThreads,int ThreadIndex/*from 1*/,long file_length, long &BgPos, long &BlkSize)long BlockSize, lastBlockSize, BeginPos;int i;BlockSize=file_length/TotalThreads;lastBlockSize=file_length;BeginPos=0;for(i=1;i<=ThreadIndex-1;

38、i+)lastBlockSize=lastBlockSize-BlockSize;BeginPos=BeginPos+BlockSize;if (ThreadIndex=TotalThreads)BgPos=BeginPos;BlkSize=lastBlockSize;elseBgPos=BeginPos;BlkSize=BlockSize;8 添加全局变量CString filename; 和 char fn40;用以储存被发送的文件的文件名把它们添加到ServerDlg.cpp文件的头部。如下:/ ServerDlg.cpp : implementation file/#include &

39、quot;stdafx.h"#include "Server.h"#include "ServerDlg.h"#include "MySocket.h"#include "process.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif/ CAboutDlg dialog used for App AboutCString filename;char fn40;9 双击“发送”

40、按钮,为它添加事件BN_CLICKED的响应函数OnSend()注意:必须在使用_beingthread()和_endthread()函数的.cpp文件的头部加上#include “process.h”void CServerDlg :OnSend ()CFileDialog fd(TRUE);if(IDOK=fd.DoModal() / 启动用于选择文件的对话框/选择了文件filename=fd.GetFileName(); /获取选择的文件的文件名strcpy(fn,filename.GetBuffer(0);else return; /按了取消按钮for(int i=0;i<5;i

41、+)_beginthread(SendThreadFunction,0,(void *)i); /启动工头线程(由工头线程启动工人线程)用传递函数参数的方式为每个线程指定序号,是因为这样可以简化程序,避免加入代码处理因使用全局变量而带来的线程同步问题。这些代码的作用是,驱动工头们到它们的岗位上(每个工头管理一个工人数据传送线程)。10 编写SendThreadFunction函数这个函数起到了工头的作用,通过参数(void*)i为每个工人线程编号和分配任务。void SendThreadFunction(void * pParam)int idx=(int)pParam;SendThread(

42、idx+1); / 启动工人线程_endthread();11 编写真正实现数据发送的函数SendThreaddefine ReadSize 500void SendThread(int idx) / 传输文件数据的函数(搬运工人)CFile file;char dataReadSize;long BeginPos, Size;long FileLength;long ReadOnce, LeftToRead, count;if(!file.Open(fn,CFile:modeRead|CFile:shareDenyNone)AfxMessageBox("read file erro

43、r!");return;FileLength=file.GetLength();sendSocketsidx-1. Send(&FileLength, 4);sendSocketsidx-1. Send(fn, 40);/ 获取本线程传输任务(传送块的大小和起始位置)GetBeginPos(5 , idx, FileLength ,BeginPos, Size); /其中的5表示总共有5个线程,idx表示本线程编号file.Seek(BeginPos, CFile:begin); /每个线程函数找到自己任务的起始点LeftToRead=Size;while(LeftToRea

44、d>0)ReadOnce=(LeftToRead>ReadSize)?ReadSize:LeftToRead;count=file.ReadHuge(data,ReadOnce);while(SOCKET_ERROR= sendSocketsidx-1.Send(data,count)LeftToRead=LeftToRead-count; file.Close();·客户端(负责接收数据)的实现1 一个基于对话框的工程,并在建立的过程中选择支持windows socket。2 在对话框上添加“连接”按钮和“接收”按钮。3 用ClassWizard添加一个派生于CAsyn

45、cSocket的类CMySocket4 定义全局句柄数组HANDLE hEvent5;5 定义CMySocket型的全局变量listenSocket和数组receiveSockets56 定义全局变量char fn40;用以储存接收文件的文件名7 为CMySocket类添加消息OnConnect的响应函数OnConnect()void CMySocket:OnConnect(int nErrorCode) / TODO: Add your specialized code here and/or call the base classstatic int File_Socket_Connect

46、ed=0;if(nErrorCode=0)File_Socket_Connected+;this->AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE);if(File_Socket_Connected=5)File_Socket_Connected=0;AfxMessageBox("连接成功");CAsyncSocket:OnConnect(nErrorCode);8 为“连接”按钮添加事件BN_CLICKED的响应函数OnConnect()void CServerDlg:OnConnect()for(int i=0;i<5;i+) r

47、eceiveSocketsi.Create();receiveSocketsi.Connect(“127.0.0.1”,7000);9 为“接收”按钮添加事件BN_CLICKED的响应函数OnReceive()void CServerDlg:OnReceive ()/以下for循环的作用是初始化事件对象为无信号的,用于监视线程识别文件传输是否结束for(int i=0;i<5;i+)if(hEventi=CreateEvent(NULL,false,false,NULL)=NULL)AfxMessageBox("Create hE-Event Handle Failed&quo

48、t;);ResetEvent(hEventi);_beginthread(ReceiveNotifyFunction,0,NULL);for(i=0;i<5;i+)_beginthread(ReceiveThreadFunction,0,(void *)i);10 编写ReceiveThreadFunction函数void ReceiveThreadFunction(void * pParam)int idx=(int)pParam;ReceiveThread(idx+1);_endthread();11 编写真正实现数据发送的函数ReceiveThreaddefine ReadSize

49、 500void ReceiveThread (int idx) / 接收文件数据的函数(搬运工人)CFile file;char dataReadSize;long BeginPos, Size;long FileLength;long WriteOnce;char filename200;sprintf(filename,"tmpsave-%d.dat",idx);if(!file.Open(filename,CFile:modeCreate|CFile:modeWrite)AfxMessageBox("write file error!");ret

50、urn;while(SOCKET_ERROR=receiveSocketsidx-1. Receive(&FileLength, 4)while(SOCKET_ERROR=receiveSocketsidx-1. Receive(fn, 40)/ 获取本线程传输任务(传送块的大小和起始位置)GetBeginPos(5 , idx, FileLength ,BeginPos, Size); /其中的5表示总共有5个线程,idx表示本线程编号while(Size>0)if(SOCKET_ERROR=(WriteOnce= receiveSocketsidx-1.Receive(dat

51、a,ReadSize)continue;Size=Size-WriteOnce;file.WriteHuge(data,WriteOnce);file.Close();SetEvent(hEventidx-1); / 发信号通知监视线程本线程任务完成12 编写监视文件接收是否结束的函数ReceiveNotifyFunctionvoid ReceiveNotifyFunction(void * pParam) / 监视文件传输的状态的线程WaitForMultipleObjects(5,hEvent,true,INFINITE);/当所有工人线程都报告自己完成任务后,准备合并文件CombineF

52、iles(); / 拼接每个线程暂存数据的文件_endthread();13 编写合并文件的函数CombineFilesvoid CombineFiles()CFile fileDest, fileSour;char sourname500;static char data10000;long count;int i;if(!fileDest.Open(fn,CFile:modeCreate|CFile:modeWrite) / 建立并打开目标文件AfxMessageBox("Combine: Make Dest File Error");return;for(i=0;i&

53、lt;5;i+) / 一次打开一个线程暂存的临时文件,把其中数据写入目标文件后,删除临时文件wsprintf(sourname,"tmpsave-%d.dat",i+1);if(!fileSour.Open(sourname,CFile:modeRead)AfxMessageBox("Combine: Open Part File Error");return;count=fileSour.ReadHuge(data,10000);while(count>0)fileDest.WriteHuge(data,count);count=fileSour.ReadHuge(data,10000);fileSour.Close();if(!DeleteFile(sourname)AfxMessageBox("Combine: File-Delete Error");return;fileDest.Close();上述的多线程传输程序的传输速度已经相

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论