1、 下载工具的设计与开发 摘要 在BT软件以及电驴等P2P工具日益发展的今天,不管你有多少带宽他都会100%的占用。这类P2P软件的下载速度完全依赖带宽,然而,如何在不提高带宽的前提下提高局域网的下载速度是一个符合实际的问题,本文就基于该问题提出并实现了“分布式下载工具”系统。该系统很好的解决了局域网被限速的问题,大大提高了下载速度。 “分布式下载工具”联合局域网内的几台客户机同时去下载Internet网上的同一资源的不同数据块,下载完后按顺序统一整合。它有下载客户机与服务器的功能,分别运用VC和VB.Net开发设计。客户机完成下载与连接其他用户的功能;而
2、服务器则管理用户和资源情况,供客户机提供在线用户的数据,并且服务器能够对用户和资源进行控制。比如:封锁某些不合法的资源、禁止某些不合法的用户使用等。 本系统设计完成后,并与其他下载软件进行了测试比较,提升效果明显,有一定推广使用价值。 关键词:下载速度;下载软件;局域网 The Development of Distributed Download Tool ABSTRACT P2P download tools like BT and e-Mule became increasingly developed nowada
3、ys. No matter how much bandwidth you have, 100 percent fully of it will be the occupied. Download speed of such p2p software is totally depends on the bandwidth. However, how to boost speed of LAN on the actuality of bandwidth is a practical issue .this article expatiates from such issue and actuali
4、ze “distributed download tool” system. This system has a very good solution to the limited LAN, strongly improving the download speed. The "Distributed download tool”, is a technology to join several clients at the same time to download different data blocks from the some resources on the Internet
5、 and each client reunifies these blocks a completed file. The system can act as client and server both, respectively, with VC and VB.Net to exploit and design. As a client it does download work and connects with other users; while being the server, it manages the users and resources for the client
6、and provides online data. Besides, the server can control users and resources. Compared with other download software, a completed “Distributed download tool” system upgrades download speed significantly and is of good popularization value. Key words: Download Speeds; Download Software; Local
7、 Area Networks 目录 第一章 前言 1 1.1项目简单介绍 1 1.2 项目特点 1 1.3 项目开发背景 2 1.4 项目的创新性 2 1.5 项目开发的总目标 2 第二章 需求分析 3 2.1 功能需求 3 2.2 性能需求 3 2.3 系统数据流图 4 2.3.1 系统总体数据流图 4 2.3.2 服务器端数据流图 5 2.3.3客户端数据流图 6 第三章 概要设计 9 3.1 系统结构设计 9 3.1.1 系统结构示意图 9 3.1.2 系统组织模块结构图 9 3.1.3 系统HIPO表 10
8、 3.2 接口设计 11 3.2.1 用户界面接口 11 3.2.2 软件系统接口 13 3.2.3 软件内部接口 13 3.3 数据库设计 13 第四章 详细设计 15 4.1基本设计概念和处理流程 15 4.2 关键技术代码 17 第五章 系统的关键技术和难点 28 5.1 MFC消息传递机制技术 28 5.2 VC Socket通讯技术 28 5.3 VC多线程技术 29 第六章 软件测试与改进思想 30 6.1 软件测试 30 6.2 软件改进思想 31 第七章 安装与使用说明 32 7.1 服务器端安装说明 32 7.2 服务器端操作说明 34
9、7.3 客户端的安装说明 38 7.4 客户端使用说明 41 参考文献 44 结束语 46 致谢 47 分布式下载工具的设计与开发 第一章 前言 1.1项目简单介绍 本下载工具是联合局域网内的几台客户机同时去下载Internet网上的同一资源的不同数据块,下载完后按顺序统一整合。它由下载客户机与服务器两个部分组成,其中客户机完成下载与连接其他用户的功能;而服务器则管理用户和资源情况,供客户机提供在线用户的数据,并且服务器能够对用户和资源进行控制。比如:封锁某些不合法的资源、禁止某些不合法的用户使用等。 其原理是利用局域网内高的传输速度和解决局域网被限速问题。例如:
10、局域网的主机A要去下载Internet上某服务器的资源,那么它就会联合局域网内的几台主机(主机B,主机C,主机D等),它先请求服务器获取信息(资源大小),然后根据资源大小和联合的主机数平均分配任务,分别向服务器下载数据。在下载过程中,每台机将定时发送本机所下载的资源给主机A,直到下载完成。在下载过程中,如果主机C的平均速度与其他主机的平均下载速度相差悬殊的话,那么主机A将发出撤销主机C的请求,然后主机A重新搜索局域网内的其他主机代替主机C。若局域网内的主机都不能接受请求的话,那主机A将把主机C剩下的资源重新平均分配给正在下载的主机,这样就完成了整个下载任务,因此得到了很短的下载时间。 1.2
11、 项目特点 1. 本系统实现了多台机的联机下载。 2. 本系统能够对机器的性能进行分析,然后调整每台机的下载量。 3. 本系统能够检测客户端机器突然死机或者关闭的情况,对其释放,然后重新寻找新的客户端进行下载剩下的资源。 4. 本系统能够对每个资源进行管理,当有遇到一些不合法的资源时,可对其资源进行封闭。 5. 本系统能够有效管理客户端的使用,当有客户进行一些非法资源的传播时,可对他进行封锁。 1.3 项目开发背景 随着网络的发展以及各种应用软件的出现,我们可以通过网络实现与远方亲人进行电话聊天,与朋友进行互动游戏。因此相应的网络带宽也越来越受到人
12、们的重视,人们一般通过租用更多的带宽来提高网络速度。 但是在BT软件以及电驴等P2P工具日益发展的今天,不管你有多少带宽他都会100%的占用。所以目前,如何封锁P2P软件逐渐成为局域网网络管理员头疼的问题,很多局域网都将这个问题转换为带宽限制,他们通过一些软件和路由的控制有效地限制了网络带宽,从而实现对P2P软件的控制,大大影响了P2P下载软件的下载速度,效果非常显著。但是我们总是追求更高品质的网络生活,希望下载速度能够“再快一点”。对此,我将根据这些存在的问题设计出了此系统,大大提高了下载速度。 1.4 项目的创新性 1. 本软件能够主动连接其他机器分块进行下载,从而不会出现下载重复资
13、源的现象,而其他P2P软件则是被动的,避免不了下载重复资源。 2. 本软件能够有效达到了资源共享,充分利用了局域网内高的传输速度的特点,达到了资源的高效利用。 1.5 项目开发的总目标 系统开发的总目标是:充分利用局域网内的高速的传输速率与解决局域网内被限速带来的下载速度慢的问题来提高他们的下载速度。 第二章 需求分析 2.1 功能需求 根据校园网特点和目前的下载工具状况,我对这个系统的需求具有以下几个主要功能: 1. 单机下载因特网上的资源。 2. 连接局域网内的其他用户一起下载因特网上的资源。 3. 当遇到本局域网内有其他用户曾下载过的资源,系
14、统能够识别它,然后直接从本局域网下载此资源。 针对以上几个主要功能,可把本系统分为两大模块:分布式下载工具客户端和分布式下载工具服务器端。其中分布式下载工具客户端又可分为发起下载管理模块和接受下载管理模块。 2.2 性能需求 本系统是利用局域网内每台机互传的速度快的原理,并且是为了解决局域网内被限速导致下载速度慢的问题,所以本系统在一个局域网内并且该局域网内的IP被限了速度的环境下使用时,效果将会达到相当明显。 2.3 系统数据流图 2.3.1 系统总体数据流图 图2.1 系统总体数据流图 从图2.1系统总
15、体数据流图可看出,客户端新建下载任务首先向服务器端发送命令,等待服务器端返回的可供联机的地址,然后客户端根据这些地址向客户端发送联机请求,等待客户端回应,最后一起参与下载。 2.3.2 服务器端数据流图 图2.2 服务器端顶层数据流图 由上图可看出,客户端发送命令到服务器端,服务器端接收命令,然后对命令的一次处理,处理完后把相应的数据存入数据库,然后再从数据库取出数据,然后把数据经过一次命令处理后把他转为命令,最后把命令发送给客户端。 图2.3 系统第二层数据流图 服务器端第二层数据流图如上图所示,命令在处理之后分为6种不同处理过程,系统分别对不同的处理过程来操作
16、数据库,并把所得的结果经过命令格式化转变为命令,最后发送给客户端。 2.3.3客户端数据流图 图2.4 客户端顶层数据流图 由图2.4可看出客户端下载任务时,向服务器端发出请求,获取可供联机的用户,然后进行联机区下载。 图2.5 客户端第二层数据流图 由图2.5可看出,客户端与服务器,客户端与客户端之间都分别进行命令分析,对不同的命令进行相应的操作。 第三章 概要设计 3.1 系统结构设计 3.1.1 系统结构示意图 图3.1 系统结构示意图 3.1.2 系统组
17、织模块结构图 图3.2 系统组织模块结构图 3.1.3 系统HIPO表 (a) 发起下载管理模块IPO表 (b) 接收下载管理模块IPO表 (C)服务器管理模块 图3.3 系统HIPO表 3.2 接口设计 3.2.1 用户界面接口 本系统以一个友好而简单的界面呈现给用户,如下图: 图3.4 客户端界面图 资源列表区 用户列表区 信息显示区 资源搜索区 图3.5 客户端界面图 3.2.2 软件系统接口 (1)客户端与客户端之间的通讯。采用TCP通讯协议。 发起下载客户端发送
18、给接收下载客户端之间的命令格式: URL Startbytes Endbytes # 结束字节 开始字节 资源的url地址 结束标志 (2)客户端与服务器端之间的通讯。采用TCP通讯协议。 客户端发送给服务器端的命令格式: Status IP # 状态 客户端IP地址 其中状态包括:ON 系统启动 END 系统关闭 NEW 新建下载任务 CHANGE 改变用户。 服务器返回客户端的命令格式: IP …/…/… # 有效的IP地址,多个IP之间用”/”分开 结束标志 3.2.3 软件内部接口 表 3.1
19、主要的模块接口表 接口名称 传递参数 接口说明 CHttpDowndLoad::DownLoad() 下载基本信息 下载服务器资源 CHttpDowndLoad::TestLink() url 测试能否连接服务器 CHttpDowndLoad::MTConnectThread() 用户IP地址 连接参与下载的用户 CHttpDowndLoad::MTServerThreadAccept() 无 创建监听对象 CHttpDowndLoad::SendData() 连接套接字 发送数据 3.3 数据库设计 表 3.2 UserIP 用户信息表 字段名
20、 类型 说明 备注 UserID Int 用户ID号 主键,自动标识 UserIP Varchar(16) 用户IP地址 - Status Int 用户状态 -1为封锁 0为离 线,1为在线 表3.3 UserDownInfo 用户下载资源信息表 字段名 类型 说明 备注 InfoID Int 资源ID 主键,自动标识 UserIP Varchar(16) 用户IP地址 - URL Varchar 资源下载的url地址 - Path Varchar 资源存放路径 - FileName Varchar 资源存放名
21、称 - DownTime Date 下载时间 - FinishFlag Int 完成标志 0为未完成,1为完成 Forbid Int 封锁标志 -1为封锁,0为正 常 第四章 详细设计 4.1基本设计概念和处理流程 创建下载任务模块表示需要下载某一资源的用户所用的模块,其工作流程图: 图4.1 发起下载管理模块工作流程图 接收下载命令模块表示接收发起机的下载命令进行下载,其流程图如图4.2 图4.2 接收下载管理模块工作流程图 用户IP管理服务器,用来管理在
22、线用户,提供客户机可参与下载的用户IP信息。其工作流程图如下: 图4.3 服务器端工作流程图 4.2 关键技术代码 1. 下载资源代码 UINT CHttpDowndLoad::DownLoad(LPVOID pParam) { CHttpSocket HttpSocket1; IDInfo *IdInfo; IdInfo=new IDInfo; IdInfo=(IDInfo *)pParam; const char *pRequestHeader = NULL; char *pResponseHeader=NULL; char *pAcc
23、eptType=NULL; long nLength; DWORD dwServiceType; CString strServer; CString strObject; unsigned short nPort; //得到视图类的指针 // CDownLoadView* dlv; CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd; // Get the active MDI child window. CMDIChildWnd *pChild=(CMD
24、IChildWnd *) pFrame->GetActiveFrame(); // Get the active view attached to the active MDI child window. POSITION pos=pChild->GetActiveDocument()->GetFirstViewPosition(); CDownLoadView *pView=(CDownLoadView*)pChild->GetActiveDocument()->GetNextView(pos); CFile* DownloadFile; //打开在St
25、artHttpDownLoad()中创建的文件 DownloadFile=new CFile; DownloadFile->Open(pView->m_taskdowninfo[IdInfo->TaskID].savepath+pView->m_taskdowninfo[IdInfo->TaskID].filename,CFile::modeWrite|CFile::shareDenyNone); HttpSocket1.CloseSocket; AfxParseURL((LPCTSTR)(pView->m_taskdowninfo[IdInfo->TaskID].url),
26、dwServiceType,strServer, strObject, nPort); pRequestHeader = HttpSocket1.FormatRequestHeader((LPTSTR)(LPCTSTR)strServer,(LPTSTR)(LPCTSTR)strObject,nLength,NULL,NULL,pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo[IdInfo->ThreadID].FromBytes,pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo
27、[IdInfo->ThreadID].ToBytes,0,NULL); DownloadFile->Seek(pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo[IdInfo->ThreadID].FromBytes,CFile::begin); //设置文件指针位置*/ HttpSocket1.Socket(); HttpSocket1.Connect((LPTSTR)(LPCTSTR)strServer,nPort); HttpSocket1.SendRequest(); pResponseHeader=Http
28、Socket1.GetResponseCharPoint(); int nSvrState = HttpSocket1.GetServerState(); CString csState; csState.Format("%d",nSvrState); csState=csState.Left(1); if(csState=="2") { char pData[5024]; //用于存放接收数据的字符数组 long nReceSize = 0; //实际接收数据的长度(服务器返回来的值) CString fpath; long rs
29、ize,sendsize;
rsize=0;
sendsize=0;
long nsize;
char buf1[5024]; nsize=pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo[IdInfo->ThreadID].ToBytes-pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo[IdInfo->ThreadID].FromBytes;
while(rsize 30、eceSize = HttpSocket1.Receive(pData,5024);
if(nReceSize<=0)
{
HttpSocket1.CloseSocket(); //没有可以接收的数据,关闭Socket
AfxMessageBox("没有可接收的数据");
break; //return while(TRUE) !
}
DownloadFile->Write(pData,nReceSize);
31、 rsize=rsize+nReceSize;
pView->m_taskdowninfo[IdInfo->TaskID].finishsize+=nReceSize;
}
DownloadFile->Close(); pView->m_taskdowninfo[IdInfo->TaskID].ThreadDownInfo[IdInfo->ThreadID].FinishFlag=true;
::PostMessage(pView->m_hWnd,WM_SENDDATA,0,(long)&IdInfo);
}
return 0;
}
2 32、 连接其他用户代码
UINT CHttpDowndLoad::MTConnectThread(LPVOID pParam)
{
//得到视图类的指针
// CDownLoadView* dlv;
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
// Get the active MDI child window.
CMDIChildWnd *pChild=(CMDIChildWnd *) pFrame->GetActiveFrame();
// Get the 33、active view attached to the active MDI child window.
POSITION pos=pChild->GetActiveDocument()->GetFirstViewPosition();
CDownLoadView *pView=(CDownLoadView*)pChild->GetActiveDocument()->GetNextView(pos);
IDInfo *IdInfo;
IdInfo=new IDInfo;
IdInfo=(IDInfo*)pParam;
char buf[1024]; 34、
sockaddr_in addr;
addr.sin_addr.S_un.S_addr = inet_addr(pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].UserIP);
addr.sin_family=AF_INET;
addr.sin_port=LOCAL_PORT;
WSADATA wsaData;
WSAStartup(0x101,&wsaData);
SOCKET g_ConnectSocket;
g_ConnectSocket = socket(AF_I 35、NET, SOCK_STREAM,0);
if(g_ConnectSocket==INVALID_SOCKET)
{
return -1;
}
if(connect(g_ConnectSocket,(sockaddr*)&addr,sizeof(addr))==-1)
{
AfxMessageBox("无法连接到客户机");
return -1;
}
pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].s=g_ConnectSocket;
CString te 36、mp,finishsize,fromsize;
fromsize.Format("%d",pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].FromBytes);
finishsize.Format("%d",pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].ToBytes);
temp="URL:";
temp+=pView->m_taskdowninfo[IdInfo->TaskID].url;
37、
temp+="lfromsize:";
temp+=fromsize;
temp+="lfinishsize:";
temp+=finishsize;
temp+="#";
strcpy(buf,temp);
int slen;
slen=0;
int tlen;
tlen=temp.GetLength();
while(slen 38、0;
while(true)
{
rlen2=recv(g_ConnectSocket,buf2,100,0);
temp2=buf2;
if(temp2.Find("cmd:recv")>=0)
{ break;
}
else if(temp2.Find("cmd:end")>=0)
{
closesocket(g_ConnectSocket);
::PostMessage(pView->m_hWnd,WM_CHANGEUSR,(long)IdInfo,0);
return 0;
}
el 39、se
{
CString speed2,finishsize2,time2;
ParseBuf2(buf2,finishsize2,speed2,time2);
pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].Rate=finishsize2;
pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].Speed=speed2;
40、 pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].Time=time2;
}
}
CFile* rfile; //打开在StartHttpDownLoad()中创建的文件
rfile=new CFile;
rfile->Open(pView->m_taskdowninfo[IdInfo->TaskID].savepath+pView->m_taskdowninfo[IdInfo->TaskID].filename,CFile::modeWrite|CFile::shareDeny 41、None);
rfile->Seek(pView->m_taskdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].FromBytes,CFile::begin); //设置文件指针位置
long rlength,len,buflen,startsize;
rlength=0;
char buf1[5024];
while(rlength 42、skdowninfo[IdInfo->TaskID].UserDownInfo[IdInfo->ThreadID].FromBytes)
{
len=recv(g_ConnectSocket,buf1,5024,0);
rfile->Write(buf1,len);
rlength+=len; pView->m_taskdowninfo[IdInfo->TaskID].finishsize+=pView->m_taskdowninfo[IdInfo->TaskID].finishsize;
}
rfile->Close;
return 0;
}
3. 43、 点对点互传代码
UINT CHttpDowndLoad::SendFile(LPVOID pParam)
{
//得到视图类的指针
// CDownLoadView* dlv;
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
// Get the active MDI child window.
CMDIChildWnd *pChild=(CMDIChildWnd *) pFrame->GetActiveFrame();
// Get the active vi 44、ew attached to the active MDI child window.
POSITION pos=pChild->GetActiveDocument()->GetFirstViewPosition();
CDownLoadView *pView=(CDownLoadView*)pChild->GetActiveDocument()->GetNextView(pos);
ConnectInfo *ConInfo;
ConInfo=(ConnectInfo *)pParam;
long FileSize;
long ReadSize;
l 45、ong SendSize;
long TempSize;
CString temp;
CString SFileSize;
char buf[128];
char data[5*1024];
CFile pFile;
if(pFile.Open(ConInfo->Path,CFile::modeRead|CFile::shareDenyNone))
{
FileSize=pFile.GetLength();
SFileSize.Format("%d",FileSize);
temp="STATUS:HAVESIZE:";
t 46、emp+=SFileSize;
temp+="#";
strcpy(buf,temp);
send(ConInfo->s,buf,temp.GetLength(),0);
ReadSize=0;
SendSize=0;
while(SendSize 47、STATUS:NONESIZE:#";
strcpy(buf,temp);
send(ConInfo->s,buf,temp.GetLength(),0);
return 0;
}
return 0;
}
4.服务器命令处理代码
Public Function DoEvent(ByVal str As String, ByVal ss As Socket) As Boolean
Dim dv As DataView
Dim userip As String
Dim status As String
48、 status = str.Substring(str.IndexOf("STATUS:") + 7, str.IndexOf("IP:") - str.IndexOf("STATUS:") - 7)
Select Case status
Case "START" '用户启动命令
userip = str.Substring(str.IndexOf("IP:") + 3, str.IndexOf("#") - str.IndexOf("IP:") - 3)
dv = db.R 49、unSQLAsDataView("select * from UserIP where UserIP like '%" + userip + "%'") '查询用户信息是否已经在数据库
If dv.Count > 0 Then '如果用户已经存在,则直接更新用户状态为在线
db.RunDelOrInsSQL("update UserIP set status='1' where UserIP like '%" + userip + "%'")
Else '如果用户不存在,则插入用 50、户IP并且置状态为在线
db.RunDelOrInsSQL("insert into UserIP(UserIP,status) values('" + userip + "','1')")
End If
RefreshUserList()
'txbShowInfo控件显示用户上线信息
ShowData("用户" + userip + "上线了")
Case "CLOSE" '用户离开






