VC写的远程监控程序

这学期什么都没学,一直在钻研VC,平时上课,老师在上面讲着,我就躲到后排,耳朵里赛个MP3,埋头看我的VC..马上就要开始考试了,居然也不紧张,看来自己已经成老油条了,也不怕什么挂科了(又不是没挂过,所以不在乎了)..

好了,费话不多说,我就说说我是怎么做这个程序的吧..其实这也谈不上是什么远程监控,好的远程监控软件,自己到网上看看报价就知道了,所以,我这个只能叫程序,不能称为软件...

这个程序共分为两部分:服务端(Server)和客户端(Client),程序能完成下面三大功能:

1.监视服务端主机的桌面,并传输给客户端显示.

2.录制服务端的声音,发送给客户端播放..

3.文件传输,能在客户端显示服务端的主机目录,类似windows资源管理器,并能进行文件传输.

当初在设计时,是想这个程序还能完成控制,就如同QQ的远程协助,可惜在调试程序时很麻烦,因为调试程序时,服务端和客户端都在自己机子上,不好看效果..所以为了省事,就跳过了这部分的功能,但这部分应该也不难,主要是两个API函数,模拟鼠标和按键功能,一个是VOID mouse_event(),一个是VOID keybd_event(),具体的使用你可以查看MSDN..有兴趣的朋友可以添加上这个功能,还可以加上别的功能,比如远程关机,注销等..下面我就按照上面的顺序,讲下如何实现这三大功能吧,程序的源码我已经传到CSDN的资源区,需要的朋友可以下来看看..http://download.csdn.net/source/513832..

1.监视服务端主机的桌面,并传输给客户端显示.

先从服务端说起吧,我在服务端创建了个类CServer,主要完成这些事情1、监听客户端的命令 2、向客户传送桌面画面 3、向客户端传送服务器语音数据 4、向客户端传送文件.

如果你没有一点网络编程基础,推荐你看本书《Visual C++ 网络通信编程实用案例精选》 人民邮电出版..建议先依照书上的例子写个点对点的聊天程序,熟悉下WinSock API..我个人认为,网络上两台主机间的通信就像是我们生活中的打电话,比如,我现在想给我的一个同学打电话,我首先要知道他们宿舍的电话号码,这个号码就对应网络上的IP地址,现在电话通了,可他们宿舍有6个人,该和谁通信呢??我当然会说**同学在吗,这个就相当于端口号..有了IP地址和端口号,网络上的两台电脑就可以实现通信了..这个比喻可能不是很恰当,但我目前对网络编程的理解就是这样的..

先来看看CServer类的数据成员和成员函数吧:

class CServer

{

	//私有成员函数

private: 

	void InitCommon(); //初始化,创建好socket,准备好与客户端连接,用于一般通信

	void InitSocket(SOCKET &socketForListen, int nPort); //初始化socket函数,参数传递的是socket和端口

	void InitVideo(); //初始化,创建好socket,准备好传送桌面画面

	void InitAudio(); //初始化,创建好socket,准备好传送主机声音

	void InitSendFile(); //初始化,创建好socket,准备好传送文件



	//------------------------------

	//说明,ScrBmpToFile()这个函数,我当时的想法是把屏幕的画面先保存在本机的磁盘上,

	//然后再以文件的形式发送到客户端,而客户端再读位图文件,显示画面.发现这样做效果不是很好,

	//所以就放弃了,这个函数就是这样留下的.

	//------------------------------

	void ScrBmpToFile(); //将屏幕图像保存为位图文件



	void CatchScrBmp(); //捕获屏幕位图结构和数据





	//公有成员函数

public:

	CServer(CServerDlg* pDlg);

	~CServer();

	void SendMsg(); //向客户端发送信息

	void StartSendVideo(); //开始传送主机桌面数据

	void StartSendAudio(); //开始传送主机语音信息

	void StartSendFile(CString strFilePath); //传送主机文件,参数为文件位置

	void SendScrBmp(); //传送桌面画面



	void Record(); //开始录音

	void Pause(); //暂停录音

	void SendAudioData(); //发送音频数据



	void SendLocalDrives(); //传送本机的驱动器信息

	void SendFolderInfo(CString strPath); //发送strPath目录下的所有文件和文件夹

	void SendFileInfo(CString strPath); //发送文件的基本信息,路径为strPath



	

	//私有数据成员

private:

	int m_nCommonPort; //用于一般通信的端口,一般通信包括文件传输,监听客户端命令

	int m_nAudioPort; //用于语音通信的端口

	int m_nVideoPort; //用于传播服务器屏幕画面的端口

	int m_nSendFilePort; //用于传送文件的端口

	

	//线程句柄

	HANDLE m_hCommon; //线程句柄,用于普通通信

	HANDLE m_hVideo; //线程句柄,用于传送桌面画面

	HANDLE m_hAudio; //线程句柄, 用于传送主机声音

	



	BITMAP m_bmpBit; //桌面位图结构

	char *m_pBmpData; //桌面位图数据



	CSound *m_pSound; //服务器的声音



	



	//公有数据成员

public:

	CServerDlg* m_pDlg; //程序窗口指针	



	SOCKET m_socketListenForCommon; //监听套接字,为普通通信准备

	SOCKET m_socketListenForAudio; //监听套接字,为传送语音准备

	SOCKET m_socketListenForVideo; //监听套接字,为传送屏幕画面准备

	SOCKET m_socketListenForSendFile; //监听套接字,为传送文件准备



	SOCKET m_socketRealConversationForCommon; //真正会话套接字,为普通通信准备

	SOCKET m_socketRealConversationForAudio; //真正会话套接字,为传送语音准备

	SOCKET m_socketRealConversationForVideo; //真正会话套接字,为传送屏幕画面准备

	SOCKET m_socketRealConversationForSendFile; //真正会话套接字,为传送文件准备



	sockaddr_in m_sockaddrServer; //服务器地址

	CString m_strMsg; //需要发送的消息



	//bool m_bEndThreadCommon; //结束普通通信线程

	bool m_bEndThreadVideo; //结束传送桌面画面线程

	bool m_bEndThreadAudio; //结束传送语音数据

	bool m_bEndThreadSendFile; //结束传送文件





	CString m_strFilePath; //要传输的文件路径

};

 服务端在初始化时,先启动个线程,一直监听客户端的命令,我这里自己定义了些命令

EXIT  客户端关闭了应用程序;           

VIDEO  客户端请求查看主机的桌面画面 ;         VIDEO_CLOSE  客户端要求停止传送桌面画面;      

AUDIO  客户端请求监听主机的声音;                  AUDIO_CLOSE  客户端要求停止传送主机的声音;  

DRIVES 客户端要求查看主机的磁盘驱动器;    

 FOLDER|strPath|  客户端要求查看文件夹下的目录,命令后面紧跟的是文件夹路径

 FILE|strPath|  客户端要求查看一个文件的详细信息,命令后面紧跟的是文件路径

SEND_FILE|strPath| 客户端要求把主机的一个文件传过来,命令后面紧跟的是文件路径

SEND_FILE_CANCEL 客户端中断了传送

 服务端在监听到各自的命令,完成相应的动作..现在服务端收到命令VIDEO  客户端请求查看主机的桌面画面 ; 则创建个线程,传送主机画面,下面是传送画面的线程函数

//线程函数,用于传送主机桌面画面

UINT ThreadFuncVideo(LPVOID pParam)

{

	CServer *pServer = (CServer *)pParam;



	int nLength = sizeof(pServer->m_sockaddrServer);

	pServer->m_socketRealConversationForVideo = accept(pServer->m_socketListenForVideo, (sockaddr *)&pServer->m_sockaddrServer, &nLength);

	

	if(pServer->m_socketRealConversationForVideo == INVALID_SOCKET)

	{

		pServer->m_pDlg->MessageBox("调用accept()出错");

		return 1;

	}

	//传送画面的socket已建立

	//开始传送桌面画面

	while(true)

	{

		if(pServer->m_bEndThreadVideo == true) //结束传送桌面画面线程

			AfxEndThread(2);

		pServer->SendScrBmp(); //发送桌面画面

		

	}



	return 0;

}

这里用到了CServer的成员函数SendScrBmp(),一个专门用于传送桌面画面的函数.函数原型如下:

//发送屏幕画面函数

void CServer::SendScrBmp()

{



	CatchScrBmp(); //先捕获屏幕位图结构和数据,即得到m_bmpBit,和m_pBmpData

	//发送位图结构信息

	int nSend = send(m_socketRealConversationForVideo, (char *)&m_bmpBit, sizeof(m_bmpBit), 0);

	

	//发送位图数据信息

	int nBytesSent = 0;

	int nBytesThisTime = 0;

	char *pch = m_pBmpData;  	

	int size = m_bmpBit.bmWidthBytes * m_bmpBit.bmHeight;

	do{       //发送大量的数据时 采用循环 直到发送完要发送的数据为止 

		if(m_bEndThreadVideo == true) //结束传送桌面画面线程

			break;	

		nBytesThisTime = send(m_socketRealConversationForVideo, pch, size - nBytesSent, 0);

		nBytesSent += nBytesThisTime;

		pch += nBytesThisTime;

	}while(nBytesSent < size);			  

	delete []m_pBmpData; //发送完毕时删除位图的数据信息,清理申请的内存	 

	m_pBmpData = NULL;

}

而要发送桌面的画面,你首先要捕获桌面的画面,所以,SendScrBmp()这个函数又调用了函数CatchScrBmp(); 截屏函数,把桌面画面以位图信息的形式保存到内存中,然后再发送出去..下面是CatchScrBmp()函数的原型.

//捕获屏幕位图信息和数据

void CServer::CatchScrBmp()

{

	HDC hScrDC, hMemDC; //屏幕,内存设备描述表句柄

	HBITMAP hBmp; //位图句柄

	int nWidth, nHeight; //屏幕的宽和高

	hScrDC = ::CreateDC("DISPLAY", NULL, NULL, NULL); //创建屏幕设备描述表句柄

	hMemDC = ::CreateCompatibleDC(hScrDC);  //创建与屏幕设备相兼容的内存设备描述表

	//分别得到屏幕的宽和高

	nWidth = ::GetDeviceCaps(

		hScrDC, 

		HORZRES	//	HORZRES Width, in pixels, of the screen. 

				//  VERTRES Height, in raster lines, of the screen. 

		);

	nHeight = ::GetDeviceCaps(hScrDC, VERTRES);



	//创建与屏幕设备相兼容的位图

	hBmp = ::CreateCompatibleBitmap(

			hScrDC,        // handle to DC

			nWidth,     // width of bitmap, in pixels

			nHeight     // height of bitmap, in pixels

			);

	::SelectObject(hMemDC, hBmp);  //将位图选入内存设备描述表

	//复制屏幕设备描述表到内存设备描述表

	::BitBlt(

		  hMemDC, // handle to destination DC

		  0,  // x-coord of destination upper-left corner

		  0,  // y-coord of destination upper-left corner

		  nWidth,  // width of destination rectangle

		  nHeight, // height of destination rectangle

		  hScrDC,  // handle to source DC

		  0,   // x-coordinate of source upper-left corner

		  0,   // y-coordinate of source upper-left corner

		  SRCCOPY  // raster operation code

		);



	CBitmap Bmp;

	Bmp.Attach(hBmp);//根据位图句柄,得到位图

	BITMAP Bitmap; //位图结构

	Bmp.GetBitmap(&Bitmap); //得到位图信息头和位图颜色信息

	BITMAPINFO BitmapInfo;  //位图信息结构,包含位图信息头和位图颜色信息

	//位图信息头结构

	BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO); //本结构所占用的字节数

	BitmapInfo.bmiHeader.biWidth = Bitmap.bmWidth; //位图宽度,以像素为单位

	BitmapInfo.bmiHeader.biHeight = Bitmap.bmHeight; //位图高度,以像素为单位

	BitmapInfo.bmiHeader.biPlanes = 1; //目标设备的级别,必须为1

	BitmapInfo.bmiHeader.biBitCount = Bitmap.bmBitsPixel; //每个像素所需的位数,必须为1,4,8,24,32

	BitmapInfo.bmiHeader.biCompression = 0; //位图压缩类型,必须为0,1,2

	BitmapInfo.bmiHeader.biSizeImage = Bitmap.bmWidthBytes * Bitmap.bmHeight; //位图大小,以字节为单位

	BitmapInfo.bmiHeader.biXPelsPerMeter = 0; //位图水平分辨率,每米像素数

	BitmapInfo.bmiHeader.biYPelsPerMeter = 0; //位图垂直分辩率,每米象素数

	BitmapInfo.bmiHeader.biClrUsed = 0; //位图实际使用的颜色表中的颜色数

	BitmapInfo.bmiHeader.biClrImportant = 0; //位图显示过程中重要的颜色数



		//位图数据信息

	m_pBmpData = new char[Bitmap.bmWidthBytes * Bitmap.bmHeight];  //用于保存位图数据的缓冲区

	int n = ::GetDIBits(

		  hMemDC,           // handle to DC

		  hBmp,      // handle to bitmap

		  0,   // first scan line to set

		  Bitmap.bmHeight,   // number of scan lines to copy

		  m_pBmpData,    // array for bitmap bits

		  &BitmapInfo, // bitmap data buffer

		  DIB_RGB_COLORS        // RGB or palette index

		);

	m_bmpBit = Bitmap;



	//创建了句柄,一定要释放,否则会浪费内存

	::DeleteDC(hScrDC);

	::DeleteDC(hMemDC);

}

 好了,服务端的工作完成了,现在轮到客户端接收位图信息,并显示了..和服务端一样,客户端也要另启动个线程,一直接收服务器的位图信息.

//线程函数,用于接收主机屏幕画面

UINT ThreadFuncVideo(LPVOID pParam)

{

	CClient *pClient = (CClient *)pParam;

	while(true)

	{

		if(pClient->m_bEndThreadVideo == true) //结束传送桌面画面线程

			AfxEndThread(2);

		

		//先接收位图结构

		char BmpBuffer[24];

		int nRecv = recv(pClient->m_socketRealConversationForVideo, BmpBuffer, sizeof(BITMAP), 0);

		//得到位图结构信息

		BITMAP *pBitmap = (BITMAP *)BmpBuffer;

		//如果接收有错..跳过此次错误

		if(pBitmap == NULL)

			continue;

		pClient->m_bmpBit.bmBits = pBitmap->bmBits;

		pClient->m_bmpBit.bmBitsPixel = pBitmap->bmBitsPixel;

		pClient->m_bmpBit.bmHeight = pBitmap->bmHeight;

		pClient->m_bmpBit.bmPlanes = pBitmap->bmPlanes;

		pClient->m_bmpBit.bmType = pBitmap->bmType;

		pClient->m_bmpBit.bmWidth = pBitmap->bmWidth;

		pClient->m_bmpBit.bmWidthBytes = pBitmap->bmWidthBytes;

		//获得位图的数据

		int size = pClient->m_bmpBit.bmWidthBytes * pClient->m_bmpBit.bmHeight;

		pClient->m_pBmpData = new char[size];

		if(pClient->m_pBmpData == NULL)

		{			

			pClient->m_pView->MessageBox("faile memery");

			return 0;		

		}

		char *pch = pClient->m_pBmpData;

		int nBytesRec = 0;

		int nBytesThisTime = 0;



		do{                               //发送的内容较大采用循环发送完成为止

			if(pClient->m_bEndThreadVideo == true) //结束传送桌面画面线程

				break;

			nBytesThisTime = recv(pClient->m_socketRealConversationForVideo, pch, size - nBytesRec, 0);

			nBytesRec += nBytesThisTime;

			pch += nBytesThisTime;

		}while(nBytesRec < size);

		

		pClient->ShowBmp(); //显示刚收到的位图

		

	}

	return 0;

}

 

接收到的数据都保存在客户端类CClient的数据成员中..  BITMAP m_bmpBit; //桌面位图结构                                    char m_pBmpData; //桌面位图数据,下面是在客户端显示服务端桌面画面函数ShowBmp();

void CClient::ShowBmp()

{

	CBitmap tbitmap;

	//根据位图结构信息创建位图对象

	if(tbitmap.CreateBitmapIndirect(&m_bmpBit) == NULL) 

	{

	   m_pView->MessageBox("b mull");

	   return ;

	}

	if(tbitmap.m_hObject == NULL) 

	{

		m_pView->MessageBox("NULL");

		return ;

	}



	BITMAP Bitmap = m_bmpBit;

	BITMAPINFO BitmapInfo;  //位图信息结构,包含位图信息头和位图颜色信息

	//位图信息头结构

	BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO); //本结构所占用的字节数

	BitmapInfo.bmiHeader.biWidth = Bitmap.bmWidth; //位图宽度,以像素为单位

	BitmapInfo.bmiHeader.biHeight = Bitmap.bmHeight; //位图高度,以像素为单位

	BitmapInfo.bmiHeader.biPlanes = 1; //目标设备的级别,必须为1

	BitmapInfo.bmiHeader.biBitCount = Bitmap.bmBitsPixel; //每个像素所需的位数,必须为1,4,8,24,32

	BitmapInfo.bmiHeader.biCompression = 0; //位图压缩类型,必须为0,1,2

	BitmapInfo.bmiHeader.biSizeImage = Bitmap.bmWidthBytes * Bitmap.bmHeight; //位图大小,以字节为单位

	BitmapInfo.bmiHeader.biXPelsPerMeter = 0; //位图水平分辨率,每米像素数

	BitmapInfo.bmiHeader.biYPelsPerMeter = 0; //位图垂直分辩率,每米象素数

	BitmapInfo.bmiHeader.biClrUsed = 0; //位图实际使用的颜色表中的颜色数

	BitmapInfo.bmiHeader.biClrImportant = 0; //位图显示过程中重要的颜色数



	CDC *pDC = m_pView->GetDC();



	CDC tmemdc; 

	tmemdc.CreateCompatibleDC(pDC);

	tmemdc.SelectObject(&tbitmap);



	//SetDIBits函数功能:该函数使用指定的DIB位图中发现的颜色数据来设置位图中的像素。

	SetDIBits(tmemdc.m_hDC, tbitmap, 0, Bitmap.bmHeight, m_pBmpData, &BitmapInfo, DIB_RGB_COLORS);

	//在程序的客户区显示主机桌面

	CRect rect;

	m_pView->GetClientRect(&rect);

	pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &tmemdc,0,0,Bitmap.bmWidth, Bitmap.bmHeight,SRCCOPY);



	//释放资源

	pDC->DeleteDC();

	tmemdc.DeleteDC();

	delete []m_pBmpData;

	m_pBmpData = NULL;



}

 到目前为止,关于传送桌面画面的功能就实现了..补充一点,Windows系统好多图片都是bmp格式的,即位图..一张位图文件包括四个部分,1 位图文件头,2 位图信息头,3 位图颜色表,4 位图数据信息;具体的你自己Baidu或google下位图格式吧..我就不多说了..

 2.录制服务端的声音,发送给客户端播放..

 还是先从服务端说起,我在服务端创建了一个类CSound,专门用于声音的采集..先看看这个类吧:

class CSound

{

private:

	WAVEFORMATEX m_WaveFormat; //音频格式

	//in

	HWAVEIN	m_hWaveIn; //音频输入设备句柄	

	WAVEHDR m_wavehdrIn; //标识输入缓冲的WAVEHDR结构	

	CServerDlg *m_pDlg;  //指向窗口的指针

public:

	CHAR m_chWaveBufferIn[MAX_BUFFER_SIZE]; //保存音频数据的缓冲区

	CSound(CServerDlg *pDlg); //构造函数,传递窗口类指针

	~CSound(); 

	void Init(); //初始化,准备录音

	void Record();//开始录音

	void Pause(); //暂停录音

	void FreeBufferIn(); //释放输入音频缓冲

};

关于声音的采集,主要用到的API函数有:waveInOpen(), waveInPrepareHeader(),waveInStart(),关于这部分的用法,最好能看看我前面介绍的书,里面讲的很详细..每当一个内存块被录满时,就会向窗口发送消息MM_WIM_DATA,这样我就在CServeDlg,这个窗口类中添加了消息映射,和消息处理函数..

//当音频输入缓冲录满时,释放缓冲

void CServerDlg::OnAudioBufferFull()

{

	m_pServer->SendAudioData();

}

 下面是CServer类的成员函数SendAudioData(),发送语音数据.

//向客户端发送音频数据

void CServer::SendAudioData()

{

	//发送音频数据

	send(m_socketRealConversationForAudio, m_pSound->m_chWaveBufferIn, MAX_BUFFER_SIZE, 0);

	//释放内存块,为下次录音准备

	m_pSound->FreeBufferIn(); 

}

每当发送一个内存块时,要清空一次,为下次录音做准备.下面函数是释放输入缓冲的内存块

//当一内存块被录满时,调用此函数,为下次录音准备

void CSound::FreeBufferIn()

{

	MMRESULT result;

	//调用waveInUnprepareHeader(),释放输入音频的缓冲

	result = waveInUnprepareHeader(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR));

	if(result != MMSYSERR_NOERROR)

	{

		m_pDlg->MessageBox("调用waveInUnprepareHeader()出错");

		return ;

	}

	//再为下一次录音准备

	//调用waveInPrepareHeader(),准备输入音频的缓冲

	m_wavehdrIn.lpData = m_chWaveBufferIn;

	m_wavehdrIn.dwBufferLength = MAX_BUFFER_SIZE;

	m_wavehdrIn.dwBytesRecorded = 0;

	m_wavehdrIn.dwFlags = 0;

	result = waveInPrepareHeader(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR));

	if(result != MMSYSERR_NOERROR)

	{

		m_pDlg->MessageBox("调用waveInPrepareHeader()出错");

		return ;

	}

	//调用waveInAddBuffer(),增加内存

	result = waveInAddBuffer(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR));

	if(result != MMSYSERR_NOERROR)

	{

		m_pDlg->MessageBox("调用waveInAddBuffer()出错");

		return ;

	}

}

好了,服务端的任务完成了..剩下客户端来接收数据,并播放录音了..客户端也用了个线程,一直接收数据,然后播放

//线程函数,用于接收主机声音

UINT ThreadFuncAudio(LPVOID pParam)

{

	CClient *pClient = (CClient *)pParam;

	while(true)

	{

		if(pClient->m_bEndThreadAudio == true) //结束接收主机声音线程		

			AfxEndThread(2);

		

		char *pAudioData = new char[MAX_BUFFER_SIZE];

		recv(pClient->m_socketRealConversationForAudio, pAudioData, MAX_BUFFER_SIZE, 0);

		pClient->Play(pAudioData);

		delete []pAudioData;		

	}

	return 0;

}

 

3.文件传输,能在客户端显示服务端的主机目录,类似windows资源管理器,并能进行文件传输.

服务端:

服务端只要每次读文件,然后发送出去就行了,当然这都是要用到一个线程的

//线程函数,用于发送文件

UINT ThreadFuncSendFile(LPVOID pParam)

{

	CServer *pServer = (CServer *)pParam;

	//创建用于音频数据传输的真正会话socket

	int nLength = sizeof(pServer->m_sockaddrServer);

	pServer->m_socketRealConversationForSendFile = accept(pServer->m_socketListenForSendFile, (sockaddr *)&pServer->m_sockaddrServer, &nLength);

	if(pServer->m_socketRealConversationForSendFile == INVALID_SOCKET)

	{

		pServer->m_pDlg->MessageBox("调用accept()出错");

		return 0;

	}

		//打开文件

	CFile file;

	//先发送文件大小

	if(file.Open(pServer->m_strFilePath, CFile::modeRead) == 0) //打开文件失败

	{

		//如果打开文件失败,发送文件长度为-1

		send(pServer->m_socketRealConversationForSendFile, "-1", 10, 0); 

	}

	else  //开始发送文件

	{

		//发送文件大小

		CFileStatus FileStatus;

		file.GetStatus(FileStatus);

		char cSize[10];

		DWORD nSize = FileStatus.m_size;

		itoa(nSize, cSize, 10); 

		send(pServer->m_socketRealConversationForSendFile, cSize, 10, 0);

		

		//发送文件数据

		DWORD nSendOneTime = 1024/4; //每次只发送(1/2K)数据	

		char *pFileData = new char[nSendOneTime]; 

		DWORD nSend = 0; //已发送	

		while(nSend < nSize)

		{

			if(pServer->m_bEndThreadSendFile == true)

			{				

				break;

			}

			file.Read(pFileData, nSendOneTime); 

			nSend += send(pServer->m_socketRealConversationForSendFile, pFileData, nSendOneTime, 0);

		}	

		//释放内存

		delete []pFileData;

		//关闭文件

		file.Close();

	}	

	//文件传输完毕,关闭socket,并安全的结束线程

	closesocket(pServer->m_socketListenForSendFile);

	closesocket(pServer->m_socketRealConversationForSendFile);

	AfxEndThread(2);

	return 0;

}

服务端要做的事就这么多,下面是客户端要完成的任务:

UINT ThreadFuncRecvFile(LPVOID pParam)

{

	CClient *pClient = (CClient *)pParam;

	pClient->m_pDlg->m_bOnRecvFile = true;

	//选接收文件大小

	char cSize[10];

	DWORD nSize = 0;

	recv(pClient->m_socketRealConversationForRecvFile, cSize, 10, 0);

	nSize = atoi(cSize);  //得到文件大小

	if(nSize == -1)

	{

		pClient->m_pDlg->MessageBox("服务器打开文件失败");

		//发送消息,使按钮可用

		pClient->m_pDlg->m_bOnRecvFile = false;

		pClient->m_pDlg->SendMessage(WM_ENABLEBUTTON);

	}

	else

	{

		DWORD nRecvOneTime = 1024/4; //每次只接收1/2K的大小

		DWORD nTime = nSize/nRecvOneTime; //总共需要接收的次数

		char *pFileData = new char[nRecvOneTime];

		CFile file;

		file.Open(pClient->m_strSaveAs, CFile::modeCreate | CFile::modeWrite); //打开文件

		//pClient->m_pDlg->m_ProgressCtrlRecv.SetRange(0, nSize);

		pClient->m_pDlg->m_ProgressCtrlRecv.SetRange32(0, nSize);

		DWORD nRecv = 0;

		//数据类型转换

		char cSize[10];	

		itoa(nSize, cSize, 10);

		char cRecv[10];

		DWORD nSumRecv = 0;

		int i = 0;

		while(nSumRecv < nSize)

		{

			i++;

			if(pClient->m_bEndThreadRecvFile == true)

				break;

			nRecv = recv(pClient->m_socketRealConversationForRecvFile, pFileData, nRecvOneTime, 0);

			nSumRecv += nRecv;

			file.Write(pFileData, nRecvOneTime);

			//显示完成进度,由于文件传递速度快,每隔1000个循环才提示一次接收进度

			if(i%1000 == 0)

			{

				pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(nSumRecv);

				itoa(nSumRecv, cRecv, 10);

				pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)cRecv, (LPARAM)cSize);

			}

		}

		delete []pFileData;

		//最后再显示一次进度

		pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(nSumRecv);

		if(nSumRecv > nSize)		

			nSumRecv = nSize;

		itoa(nSumRecv, cRecv, 10);

		pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)cRecv, (LPARAM)cSize);		

		if(pClient->m_bEndThreadRecvFile == true)

			pClient->m_pDlg->MessageBox("已取消传送文件");

		else

			pClient->m_pDlg->MessageBox("文件传输完毕");



		//发送消息,使按钮可用

		pClient->m_pDlg->m_bOnRecvFile = false;

		pClient->m_pDlg->SendMessage(WM_ENABLEBUTTON);

		file.Close();

		//提示文本取消..进度条为0

		pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)NULL, (LPARAM)cSize);

		pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(0);

	}

	//文件接收完毕,关闭socket,结束线程

	closesocket(pClient->m_socketRealConversationForRecvFile);

	pClient->m_bEndThreadRecvFile = false;

	AfxEndThread(2);

	return 0;

}

到现在,这个程序基本完成了,我只说了重要的部分,其实,我这程序还有些问题没解决,比如,传送文件过程,文件传过来了,可是有些文件很数据丢失,文件打不开等错误..由于得法是自己想的,所以不知如何解决,希望有经验的朋友能告诉我,在此,先谢谢你了..!!

 

 

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值