⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cdownload.cpp

📁 一个简单的视频会议VC++MFC工程文件
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// 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 + -