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类的数据成员和成员函数吧:

  1. class CServer 
  2.  
  3.  
  4.     //私有成员函数 
  5.  
  6. private:  
  7.  
  8.     void InitCommon(); //初始化,创建好socket,准备好与客户端连接,用于一般通信 
  9.  
  10.     void InitSocket(SOCKET &socketForListen, int nPort); //初始化socket函数,参数传递的是socket和端口 
  11.  
  12.     void InitVideo(); //初始化,创建好socket,准备好传送桌面画面 
  13.  
  14.     void InitAudio(); //初始化,创建好socket,准备好传送主机声音 
  15.  
  16.     void InitSendFile(); //初始化,创建好socket,准备好传送文件 
  17.  
  18.  
  19.  
  20.     //------------------------------ 
  21.  
  22.     //说明,ScrBmpToFile()这个函数,我当时的想法是把屏幕的画面先保存在本机的磁盘上, 
  23.  
  24.     //然后再以文件的形式发送到客户端,而客户端再读位图文件,显示画面.发现这样做效果不是很好, 
  25.  
  26.     //所以就放弃了,这个函数就是这样留下的. 
  27.  
  28.     //------------------------------ 
  29.  
  30.     void ScrBmpToFile(); //将屏幕图像保存为位图文件 
  31.  
  32.  
  33.  
  34.     void CatchScrBmp(); //捕获屏幕位图结构和数据 
  35.  
  36.  
  37.  
  38.  
  39.  
  40.     //公有成员函数 
  41.  
  42. public
  43.  
  44.     CServer(CServerDlg* pDlg); 
  45.  
  46.     ~CServer(); 
  47.  
  48.     void SendMsg(); //向客户端发送信息 
  49.  
  50.     void StartSendVideo(); //开始传送主机桌面数据 
  51.  
  52.     void StartSendAudio(); //开始传送主机语音信息 
  53.  
  54.     void StartSendFile(CString strFilePath); //传送主机文件,参数为文件位置 
  55.  
  56.     void SendScrBmp(); //传送桌面画面 
  57.  
  58.  
  59.  
  60.     void Record(); //开始录音 
  61.  
  62.     void Pause(); //暂停录音 
  63.  
  64.     void SendAudioData(); //发送音频数据 
  65.  
  66.  
  67.  
  68.     void SendLocalDrives(); //传送本机的驱动器信息 
  69.  
  70.     void SendFolderInfo(CString strPath); //发送strPath目录下的所有文件和文件夹 
  71.  
  72.     void SendFileInfo(CString strPath); //发送文件的基本信息,路径为strPath 
  73.  
  74.  
  75.  
  76.      
  77.  
  78.     //私有数据成员 
  79.  
  80. private
  81.  
  82.     int m_nCommonPort; //用于一般通信的端口,一般通信包括文件传输,监听客户端命令 
  83.  
  84.     int m_nAudioPort; //用于语音通信的端口 
  85.  
  86.     int m_nVideoPort; //用于传播服务器屏幕画面的端口 
  87.  
  88.     int m_nSendFilePort; //用于传送文件的端口 
  89.  
  90.      
  91.  
  92.     //线程句柄 
  93.  
  94.     HANDLE m_hCommon; //线程句柄,用于普通通信 
  95.  
  96.     HANDLE m_hVideo; //线程句柄,用于传送桌面画面 
  97.  
  98.     HANDLE m_hAudio; //线程句柄, 用于传送主机声音 
  99.  
  100.      
  101.  
  102.  
  103.  
  104.     BITMAP m_bmpBit; //桌面位图结构 
  105.  
  106.     char *m_pBmpData; //桌面位图数据 
  107.  
  108.  
  109.  
  110.     CSound *m_pSound; //服务器的声音 
  111.  
  112.  
  113.  
  114.      
  115.  
  116.  
  117.  
  118.     //公有数据成员 
  119.  
  120. public
  121.  
  122.     CServerDlg* m_pDlg; //程序窗口指针     
  123.  
  124.  
  125.  
  126.     SOCKET m_socketListenForCommon; //监听套接字,为普通通信准备 
  127.  
  128.     SOCKET m_socketListenForAudio; //监听套接字,为传送语音准备 
  129.  
  130.     SOCKET m_socketListenForVideo; //监听套接字,为传送屏幕画面准备 
  131.  
  132.     SOCKET m_socketListenForSendFile; //监听套接字,为传送文件准备 
  133.  
  134.  
  135.  
  136.     SOCKET m_socketRealConversationForCommon; //真正会话套接字,为普通通信准备 
  137.  
  138.     SOCKET m_socketRealConversationForAudio; //真正会话套接字,为传送语音准备 
  139.  
  140.     SOCKET m_socketRealConversationForVideo; //真正会话套接字,为传送屏幕画面准备 
  141.  
  142.     SOCKET m_socketRealConversationForSendFile; //真正会话套接字,为传送文件准备 
  143.  
  144.  
  145.  
  146.     sockaddr_in m_sockaddrServer; //服务器地址 
  147.  
  148.     CString m_strMsg; //需要发送的消息 
  149.  
  150.  
  151.  
  152.     //bool m_bEndThreadCommon; //结束普通通信线程 
  153.  
  154.     bool m_bEndThreadVideo; //结束传送桌面画面线程 
  155.  
  156.     bool m_bEndThreadAudio; //结束传送语音数据 
  157.  
  158.     bool m_bEndThreadSendFile; //结束传送文件 
  159.  
  160.  
  161.  
  162.  
  163.  
  164.     CString m_strFilePath; //要传输的文件路径 
  165.  
  166. }; 
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  客户端请求查看主机的桌面画面 ; 则创建个线程,传送主机画面,下面是传送画面的线程函数

  1. //线程函数,用于传送主机桌面画面 
  2.  
  3. UINT ThreadFuncVideo(LPVOID pParam) 
  4.  
  5.  
  6.     CServer *pServer = (CServer *)pParam; 
  7.  
  8.  
  9.  
  10.     int nLength = sizeof(pServer->m_sockaddrServer); 
  11.  
  12.     pServer->m_socketRealConversationForVideo = accept(pServer->m_socketListenForVideo, (sockaddr *)&pServer->m_sockaddrServer, &nLength); 
  13.  
  14.      
  15.  
  16.     if(pServer->m_socketRealConversationForVideo == INVALID_SOCKET) 
  17.  
  18.     { 
  19.  
  20.         pServer->m_pDlg->MessageBox("调用accept()出错"); 
  21.  
  22.         return 1; 
  23.  
  24.     } 
  25.  
  26.     //传送画面的socket已建立 
  27.  
  28.     //开始传送桌面画面 
  29.  
  30.     while(true
  31.  
  32.     { 
  33.  
  34.         if(pServer->m_bEndThreadVideo == true) //结束传送桌面画面线程 
  35.  
  36.             AfxEndThread(2); 
  37.  
  38.         pServer->SendScrBmp(); //发送桌面画面 
  39.  
  40.          
  41.  
  42.     } 
  43.  
  44.  
  45.  
  46.     return 0; 
  47.  
//线程函数,用于传送主机桌面画面

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(),一个专门用于传送桌面画面的函数.函数原型如下:

  1. //发送屏幕画面函数 
  2.  
  3. void CServer::SendScrBmp() 
  4.  
  5.  
  6.  
  7.  
  8.     CatchScrBmp(); //先捕获屏幕位图结构和数据,即得到m_bmpBit,和m_pBmpData 
  9.  
  10.     //发送位图结构信息 
  11.  
  12.     int nSend = send(m_socketRealConversationForVideo, (char *)&m_bmpBit, sizeof(m_bmpBit), 0); 
  13.  
  14.      
  15.  
  16.     //发送位图数据信息 
  17.  
  18.     int nBytesSent = 0; 
  19.  
  20.     int nBytesThisTime = 0; 
  21.  
  22.     char *pch = m_pBmpData;      
  23.  
  24.     int size = m_bmpBit.bmWidthBytes * m_bmpBit.bmHeight; 
  25.  
  26.     do{       //发送大量的数据时 采用循环 直到发送完要发送的数据为止  
  27.  
  28.         if(m_bEndThreadVideo == true) //结束传送桌面画面线程 
  29.  
  30.             break;   
  31.  
  32.         nBytesThisTime = send(m_socketRealConversationForVideo, pch, size - nBytesSent, 0); 
  33.  
  34.         nBytesSent += nBytesThisTime; 
  35.  
  36.         pch += nBytesThisTime; 
  37.  
  38.     }while(nBytesSent < size);              
  39.  
  40.     delete []m_pBmpData; //发送完毕时删除位图的数据信息,清理申请的内存     
  41.  
  42.     m_pBmpData = NULL; 
  43.  
//发送屏幕画面函数

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()函数的原型.

  1. //捕获屏幕位图信息和数据 
  2.  
  3. void CServer::CatchScrBmp() 
  4.  
  5.  
  6.     HDC hScrDC, hMemDC; //屏幕,内存设备描述表句柄 
  7.  
  8.     HBITMAP hBmp; //位图句柄 
  9.  
  10.     int nWidth, nHeight; //屏幕的宽和高 
  11.  
  12.     hScrDC = ::CreateDC("DISPLAY", NULL, NULL, NULL); //创建屏幕设备描述表句柄 
  13.  
  14.     hMemDC = ::CreateCompatibleDC(hScrDC);  //创建与屏幕设备相兼容的内存设备描述表 
  15.  
  16.     //分别得到屏幕的宽和高 
  17.  
  18.     nWidth = ::GetDeviceCaps( 
  19.  
  20.         hScrDC,  
  21.  
  22.         HORZRES //  HORZRES Width, in pixels, of the screen.  
  23.  
  24.                 //  VERTRES Height, in raster lines, of the screen.  
  25.  
  26.         ); 
  27.  
  28.     nHeight = ::GetDeviceCaps(hScrDC, VERTRES); 
  29.  
  30.  
  31.  
  32.     //创建与屏幕设备相兼容的位图 
  33.  
  34.     hBmp = ::CreateCompatibleBitmap( 
  35.  
  36.             hScrDC,        // handle to DC 
  37.  
  38.             nWidth,     // width of bitmap, in pixels 
  39.  
  40.             nHeight     // height of bitmap, in pixels 
  41.  
  42.             ); 
  43.  
  44.     ::SelectObject(hMemDC, hBmp);  //将位图选入内存设备描述表 
  45.  
  46.     //复制屏幕设备描述表到内存设备描述表 
  47.  
  48.     ::BitBlt( 
  49.  
  50.           hMemDC, // handle to destination DC 
  51.  
  52.           0,  // x-coord of destination upper-left corner 
  53.  
  54.           0,  // y-coord of destination upper-left corner 
  55.  
  56.           nWidth,  // width of destination rectangle 
  57.  
  58.           nHeight, // height of destination rectangle 
  59.  
  60.           hScrDC,  // handle to source DC 
  61.  
  62.           0,   // x-coordinate of source upper-left corner 
  63.  
  64.           0,   // y-coordinate of source upper-left corner 
  65.  
  66.           SRCCOPY  // raster operation code 
  67.  
  68.  
  69.         ); 
  70.  
  71.  
  72.  
  73.     CBitmap Bmp; 
  74.  
  75.     Bmp.Attach(hBmp);//根据位图句柄,得到位图 
  76.  
  77.     BITMAP Bitmap; //位图结构 
  78.  
  79.     Bmp.GetBitmap(&Bitmap); //得到位图信息头和位图颜色信息 
  80.  
  81.     BITMAPINFO BitmapInfo;  //位图信息结构,包含位图信息头和位图颜色信息 
  82.  
  83.     //位图信息头结构 
  84.  
  85.     BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO); //本结构所占用的字节数 
  86.  
  87.     BitmapInfo.bmiHeader.biWidth = Bitmap.bmWidth; //位图宽度,以像素为单位 
  88.  
  89.     BitmapInfo.bmiHeader.biHeight = Bitmap.bmHeight; //位图高度,以像素为单位 
  90.  
  91.     BitmapInfo.bmiHeader.biPlanes = 1; //目标设备的级别,必须为1 
  92.  
  93.     BitmapInfo.bmiHeader.biBitCount = Bitmap.bmBitsPixel; //每个像素所需的位数,必须为1,4,8,24,32 
  94.  
  95.     BitmapInfo.bmiHeader.biCompression = 0; //位图压缩类型,必须为0,1,2 
  96.  
  97.     BitmapInfo.bmiHeader.biSizeImage = Bitmap.bmWidthBytes * Bitmap.bmHeight; //位图大小,以字节为单位 
  98.  
  99.     BitmapInfo.bmiHeader.biXPelsPerMeter = 0; //位图水平分辨率,每米像素数 
  100.  
  101.     BitmapInfo.bmiHeader.biYPelsPerMeter = 0; //位图垂直分辩率,每米象素数 
  102.  
  103.     BitmapInfo.bmiHeader.biClrUsed = 0; //位图实际使用的颜色表中的颜色数 
  104.  
  105.     BitmapInfo.bmiHeader.biClrImportant = 0; //位图显示过程中重要的颜色数 
  106.  
  107.  
  108.  
  109.         //位图数据信息 
  110.  
  111.     m_pBmpData = new char[Bitmap.bmWidthBytes * Bitmap.bmHeight];  //用于保存位图数据的缓冲区 
  112.  
  113.     int n = ::GetDIBits( 
  114.  
  115.           hMemDC,           // handle to DC 
  116.  
  117.           hBmp,      // handle to bitmap 
  118.  
  119.           0,   // first scan line to set 
  120.  
  121.           Bitmap.bmHeight,   // number of scan lines to copy 
  122.  
  123.           m_pBmpData,    // array for bitmap bits 
  124.  
  125.           &BitmapInfo, // bitmap data buffer 
  126.  
  127.           DIB_RGB_COLORS        // RGB or palette index 
  128.  
  129.         ); 
  130.  
  131.     m_bmpBit = Bitmap; 
  132.  
  133.  
  134.  
  135.     //创建了句柄,一定要释放,否则会浪费内存 
  136.  
  137.     ::DeleteDC(hScrDC); 
  138.  
  139.     ::DeleteDC(hMemDC); 
  140.  
//捕获屏幕位图信息和数据

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);

}

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

  1. //线程函数,用于接收主机屏幕画面 
  2.  
  3. UINT ThreadFuncVideo(LPVOID pParam) 
  4.  
  5.  
  6.     CClient *pClient = (CClient *)pParam; 
  7.  
  8.     while(true
  9.  
  10.     { 
  11.  
  12.         if(pClient->m_bEndThreadVideo == true) //结束传送桌面画面线程 
  13.  
  14.             AfxEndThread(2); 
  15.  
  16.          
  17.  
  18.         //先接收位图结构 
  19.  
  20.         char BmpBuffer[24]; 
  21.  
  22.         int nRecv = recv(pClient->m_socketRealConversationForVideo, BmpBuffer, sizeof(BITMAP), 0); 
  23.  
  24.         //得到位图结构信息 
  25.  
  26.         BITMAP *pBitmap = (BITMAP *)BmpBuffer; 
  27.  
  28.         //如果接收有错..跳过此次错误 
  29.  
  30.         if(pBitmap == NULL) 
  31.  
  32.             continue
  33.  
  34.         pClient->m_bmpBit.bmBits = pBitmap->bmBits; 
  35.  
  36.         pClient->m_bmpBit.bmBitsPixel = pBitmap->bmBitsPixel; 
  37.  
  38.         pClient->m_bmpBit.bmHeight = pBitmap->bmHeight; 
  39.  
  40.         pClient->m_bmpBit.bmPlanes = pBitmap->bmPlanes; 
  41.  
  42.         pClient->m_bmpBit.bmType = pBitmap->bmType; 
  43.  
  44.         pClient->m_bmpBit.bmWidth = pBitmap->bmWidth; 
  45.  
  46.         pClient->m_bmpBit.bmWidthBytes = pBitmap->bmWidthBytes; 
  47.  
  48.         //获得位图的数据 
  49.  
  50.         int size = pClient->m_bmpBit.bmWidthBytes * pClient->m_bmpBit.bmHeight; 
  51.  
  52.         pClient->m_pBmpData = new char[size]; 
  53.  
  54.         if(pClient->m_pBmpData == NULL) 
  55.  
  56.         {            
  57.  
  58.             pClient->m_pView->MessageBox("faile memery"); 
  59.  
  60.             return 0;        
  61.  
  62.         } 
  63.  
  64.         char *pch = pClient->m_pBmpData; 
  65.  
  66.         int nBytesRec = 0; 
  67.  
  68.         int nBytesThisTime = 0; 
  69.  
  70.  
  71.  
  72.         do{                               //发送的内容较大采用循环发送完成为止 
  73.  
  74.             if(pClient->m_bEndThreadVideo == true) //结束传送桌面画面线程 
  75.  
  76.                 break
  77.  
  78.             nBytesThisTime = recv(pClient->m_socketRealConversationForVideo, pch, size - nBytesRec, 0); 
  79.  
  80.             nBytesRec += nBytesThisTime; 
  81.  
  82.             pch += nBytesThisTime; 
  83.  
  84.         }while(nBytesRec < size); 
  85.  
  86.          
  87.  
  88.         pClient->ShowBmp(); //显示刚收到的位图 
  89.  
  90.          
  91.  
  92.     } 
  93.  
  94.     return 0; 
  95.  
//线程函数,用于接收主机屏幕画面

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();

  1. void CClient::ShowBmp() 
  2.  
  3.  
  4.     CBitmap tbitmap; 
  5.  
  6.     //根据位图结构信息创建位图对象 
  7.  
  8.     if(tbitmap.CreateBitmapIndirect(&m_bmpBit) == NULL)  
  9.  
  10.     { 
  11.  
  12.        m_pView->MessageBox("b mull"); 
  13.  
  14.        return
  15.  
  16.     } 
  17.  
  18.     if(tbitmap.m_hObject == NULL)  
  19.  
  20.     { 
  21.  
  22.         m_pView->MessageBox("NULL"); 
  23.  
  24.         return
  25.  
  26.     } 
  27.  
  28.  
  29.  
  30.     BITMAP Bitmap = m_bmpBit; 
  31.  
  32.     BITMAPINFO BitmapInfo;  //位图信息结构,包含位图信息头和位图颜色信息 
  33.  
  34.     //位图信息头结构 
  35.  
  36.     BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO); //本结构所占用的字节数 
  37.  
  38.     BitmapInfo.bmiHeader.biWidth = Bitmap.bmWidth; //位图宽度,以像素为单位 
  39.  
  40.     BitmapInfo.bmiHeader.biHeight = Bitmap.bmHeight; //位图高度,以像素为单位 
  41.  
  42.     BitmapInfo.bmiHeader.biPlanes = 1; //目标设备的级别,必须为1 
  43.  
  44.     BitmapInfo.bmiHeader.biBitCount = Bitmap.bmBitsPixel; //每个像素所需的位数,必须为1,4,8,24,32 
  45.  
  46.     BitmapInfo.bmiHeader.biCompression = 0; //位图压缩类型,必须为0,1,2 
  47.  
  48.     BitmapInfo.bmiHeader.biSizeImage = Bitmap.bmWidthBytes * Bitmap.bmHeight; //位图大小,以字节为单位 
  49.  
  50.     BitmapInfo.bmiHeader.biXPelsPerMeter = 0; //位图水平分辨率,每米像素数 
  51.  
  52.     BitmapInfo.bmiHeader.biYPelsPerMeter = 0; //位图垂直分辩率,每米象素数 
  53.  
  54.     BitmapInfo.bmiHeader.biClrUsed = 0; //位图实际使用的颜色表中的颜色数 
  55.  
  56.     BitmapInfo.bmiHeader.biClrImportant = 0; //位图显示过程中重要的颜色数 
  57.  
  58.  
  59.  
  60.     CDC *pDC = m_pView->GetDC(); 
  61.  
  62.  
  63.  
  64.     CDC tmemdc;  
  65.  
  66.     tmemdc.CreateCompatibleDC(pDC); 
  67.  
  68.     tmemdc.SelectObject(&tbitmap); 
  69.  
  70.  
  71.  
  72.     //SetDIBits函数功能:该函数使用指定的DIB位图中发现的颜色数据来设置位图中的像素。 
  73.  
  74.     SetDIBits(tmemdc.m_hDC, tbitmap, 0, Bitmap.bmHeight, m_pBmpData, &BitmapInfo, DIB_RGB_COLORS); 
  75.  
  76.     //在程序的客户区显示主机桌面 
  77.  
  78.     CRect rect; 
  79.  
  80.     m_pView->GetClientRect(&rect); 
  81.  
  82.     pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &tmemdc,0,0,Bitmap.bmWidth, Bitmap.bmHeight,SRCCOPY); 
  83.  
  84.  
  85.  
  86.     //释放资源 
  87.  
  88.     pDC->DeleteDC(); 
  89.  
  90.     tmemdc.DeleteDC(); 
  91.  
  92.     delete []m_pBmpData; 
  93.  
  94.     m_pBmpData = NULL; 
  95.  
  96.  
  97.  
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,专门用于声音的采集..先看看这个类吧:

  1. class CSound 
  2.  
  3.  
  4. private
  5.  
  6.     WAVEFORMATEX m_WaveFormat; //音频格式 
  7.  
  8.     //in 
  9.  
  10.     HWAVEIN m_hWaveIn; //音频输入设备句柄    
  11.  
  12.     WAVEHDR m_wavehdrIn; //标识输入缓冲的WAVEHDR结构  
  13.  
  14.     CServerDlg *m_pDlg;  //指向窗口的指针 
  15.  
  16. public
  17.  
  18.     CHAR m_chWaveBufferIn[MAX_BUFFER_SIZE]; //保存音频数据的缓冲区 
  19.  
  20.     CSound(CServerDlg *pDlg); //构造函数,传递窗口类指针 
  21.  
  22.     ~CSound();  
  23.  
  24.     void Init(); //初始化,准备录音 
  25.  
  26.     void Record();//开始录音 
  27.  
  28.     void Pause(); //暂停录音 
  29.  
  30.     void FreeBufferIn(); //释放输入音频缓冲 
  31.  
  32. }; 
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,这个窗口类中添加了消息映射,和消息处理函数..

  1. //当音频输入缓冲录满时,释放缓冲 
  2.  
  3. void CServerDlg::OnAudioBufferFull() 
  4.  
  5.  
  6.     m_pServer->SendAudioData(); 
  7.  
//当音频输入缓冲录满时,释放缓冲

void CServerDlg::OnAudioBufferFull()

{

	m_pServer->SendAudioData();

}

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

  1. //向客户端发送音频数据 
  2.  
  3. void CServer::SendAudioData() 
  4.  
  5.  
  6.     //发送音频数据 
  7.  
  8.     send(m_socketRealConversationForAudio, m_pSound->m_chWaveBufferIn, MAX_BUFFER_SIZE, 0); 
  9.  
  10.     //释放内存块,为下次录音准备 
  11.  
  12.     m_pSound->FreeBufferIn();  
  13.  
//向客户端发送音频数据

void CServer::SendAudioData()

{

	//发送音频数据

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

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

	m_pSound->FreeBufferIn(); 

}

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

  1. //当一内存块被录满时,调用此函数,为下次录音准备 
  2.  
  3. void CSound::FreeBufferIn() 
  4.  
  5.  
  6.     MMRESULT result; 
  7.  
  8.     //调用waveInUnprepareHeader(),释放输入音频的缓冲 
  9.  
  10.     result = waveInUnprepareHeader(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR)); 
  11.  
  12.     if(result != MMSYSERR_NOERROR) 
  13.  
  14.     { 
  15.  
  16.         m_pDlg->MessageBox("调用waveInUnprepareHeader()出错"); 
  17.  
  18.         return
  19.  
  20.     } 
  21.  
  22.     //再为下一次录音准备 
  23.  
  24.     //调用waveInPrepareHeader(),准备输入音频的缓冲 
  25.  
  26.     m_wavehdrIn.lpData = m_chWaveBufferIn; 
  27.  
  28.     m_wavehdrIn.dwBufferLength = MAX_BUFFER_SIZE; 
  29.  
  30.     m_wavehdrIn.dwBytesRecorded = 0; 
  31.  
  32.     m_wavehdrIn.dwFlags = 0; 
  33.  
  34.     result = waveInPrepareHeader(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR)); 
  35.  
  36.     if(result != MMSYSERR_NOERROR) 
  37.  
  38.     { 
  39.  
  40.         m_pDlg->MessageBox("调用waveInPrepareHeader()出错"); 
  41.  
  42.         return
  43.  
  44.     } 
  45.  
  46.     //调用waveInAddBuffer(),增加内存 
  47.  
  48.     result = waveInAddBuffer(m_hWaveIn, &m_wavehdrIn, sizeof(WAVEHDR)); 
  49.  
  50.     if(result != MMSYSERR_NOERROR) 
  51.  
  52.     { 
  53.  
  54.         m_pDlg->MessageBox("调用waveInAddBuffer()出错"); 
  55.  
  56.         return
  57.  
  58.     } 
  59.  
//当一内存块被录满时,调用此函数,为下次录音准备

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 ;

	}

}

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

  1. //线程函数,用于接收主机声音 
  2.  
  3. UINT ThreadFuncAudio(LPVOID pParam) 
  4.  
  5.  
  6.     CClient *pClient = (CClient *)pParam; 
  7.  
  8.     while(true
  9.  
  10.     { 
  11.  
  12.         if(pClient->m_bEndThreadAudio == true) //结束接收主机声音线程       
  13.  
  14.             AfxEndThread(2); 
  15.  
  16.          
  17.  
  18.         char *pAudioData = new char[MAX_BUFFER_SIZE]; 
  19.  
  20.         recv(pClient->m_socketRealConversationForAudio, pAudioData, MAX_BUFFER_SIZE, 0); 
  21.  
  22.         pClient->Play(pAudioData); 
  23.  
  24.         delete []pAudioData;         
  25.  
  26.     } 
  27.  
  28.     return 0; 
  29.  
//线程函数,用于接收主机声音

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资源管理器,并能进行文件传输.

服务端:

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

  1. //线程函数,用于发送文件 
  2.  
  3. UINT ThreadFuncSendFile(LPVOID pParam) 
  4.  
  5.  
  6.     CServer *pServer = (CServer *)pParam; 
  7.  
  8.     //创建用于音频数据传输的真正会话socket 
  9.  
  10.     int nLength = sizeof(pServer->m_sockaddrServer); 
  11.  
  12.     pServer->m_socketRealConversationForSendFile = accept(pServer->m_socketListenForSendFile, (sockaddr *)&pServer->m_sockaddrServer, &nLength); 
  13.  
  14.     if(pServer->m_socketRealConversationForSendFile == INVALID_SOCKET) 
  15.  
  16.     { 
  17.  
  18.         pServer->m_pDlg->MessageBox("调用accept()出错"); 
  19.  
  20.         return 0; 
  21.  
  22.     } 
  23.  
  24.         //打开文件 
  25.  
  26.     CFile file; 
  27.  
  28.     //先发送文件大小 
  29.  
  30.     if(file.Open(pServer->m_strFilePath, CFile::modeRead) == 0) //打开文件失败 
  31.  
  32.     { 
  33.  
  34.         //如果打开文件失败,发送文件长度为-1 
  35.  
  36.         send(pServer->m_socketRealConversationForSendFile, "-1", 10, 0);  
  37.  
  38.     } 
  39.  
  40.     else  //开始发送文件 
  41.  
  42.     { 
  43.  
  44.         //发送文件大小 
  45.  
  46.         CFileStatus FileStatus; 
  47.  
  48.         file.GetStatus(FileStatus); 
  49.  
  50.         char cSize[10]; 
  51.  
  52.         DWORD nSize = FileStatus.m_size; 
  53.  
  54.         itoa(nSize, cSize, 10);  
  55.  
  56.         send(pServer->m_socketRealConversationForSendFile, cSize, 10, 0); 
  57.  
  58.          
  59.  
  60.         //发送文件数据 
  61.  
  62.         DWORD nSendOneTime = 1024/4; //每次只发送(1/2K)数据     
  63.  
  64.         char *pFileData = new char[nSendOneTime];  
  65.  
  66.         DWORD nSend = 0; //已发送   
  67.  
  68.         while(nSend < nSize) 
  69.  
  70.         { 
  71.  
  72.             if(pServer->m_bEndThreadSendFile == true
  73.  
  74.             {                
  75.  
  76.                 break
  77.  
  78.             } 
  79.  
  80.             file.Read(pFileData, nSendOneTime);  
  81.  
  82.             nSend += send(pServer->m_socketRealConversationForSendFile, pFileData, nSendOneTime, 0); 
  83.  
  84.         }    
  85.  
  86.         //释放内存 
  87.  
  88.         delete []pFileData; 
  89.  
  90.         //关闭文件 
  91.  
  92.         file.Close(); 
  93.  
  94.     }    
  95.  
  96.     //文件传输完毕,关闭socket,并安全的结束线程 
  97.  
  98.     closesocket(pServer->m_socketListenForSendFile); 
  99.  
  100.     closesocket(pServer->m_socketRealConversationForSendFile); 
  101.  
  102.     AfxEndThread(2); 
  103.  
  104.     return 0; 
  105.  
//线程函数,用于发送文件

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;

}

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

  1. UINT ThreadFuncRecvFile(LPVOID pParam) 
  2.  
  3.  
  4.     CClient *pClient = (CClient *)pParam; 
  5.  
  6.     pClient->m_pDlg->m_bOnRecvFile = true
  7.  
  8.     //选接收文件大小 
  9.  
  10.     char cSize[10]; 
  11.  
  12.     DWORD nSize = 0; 
  13.  
  14.     recv(pClient->m_socketRealConversationForRecvFile, cSize, 10, 0); 
  15.  
  16.     nSize = atoi(cSize);  //得到文件大小 
  17.  
  18.     if(nSize == -1) 
  19.  
  20.     { 
  21.  
  22.         pClient->m_pDlg->MessageBox("服务器打开文件失败"); 
  23.  
  24.         //发送消息,使按钮可用 
  25.  
  26.         pClient->m_pDlg->m_bOnRecvFile = false
  27.  
  28.         pClient->m_pDlg->SendMessage(WM_ENABLEBUTTON); 
  29.  
  30.     } 
  31.  
  32.     else 
  33.  
  34.     { 
  35.  
  36.         DWORD nRecvOneTime = 1024/4; //每次只接收1/2K的大小 
  37.  
  38.         DWORD nTime = nSize/nRecvOneTime; //总共需要接收的次数 
  39.  
  40.         char *pFileData = new char[nRecvOneTime]; 
  41.  
  42.         CFile file; 
  43.  
  44.         file.Open(pClient->m_strSaveAs, CFile::modeCreate | CFile::modeWrite); //打开文件 
  45.  
  46.         //pClient->m_pDlg->m_ProgressCtrlRecv.SetRange(0, nSize); 
  47.  
  48.         pClient->m_pDlg->m_ProgressCtrlRecv.SetRange32(0, nSize); 
  49.  
  50.         DWORD nRecv = 0; 
  51.  
  52.         //数据类型转换 
  53.  
  54.         char cSize[10];  
  55.  
  56.         itoa(nSize, cSize, 10); 
  57.  
  58.         char cRecv[10]; 
  59.  
  60.         DWORD nSumRecv = 0; 
  61.  
  62.         int i = 0; 
  63.  
  64.         while(nSumRecv < nSize) 
  65.  
  66.         { 
  67.  
  68.             i++; 
  69.  
  70.             if(pClient->m_bEndThreadRecvFile == true
  71.  
  72.                 break
  73.  
  74.             nRecv = recv(pClient->m_socketRealConversationForRecvFile, pFileData, nRecvOneTime, 0); 
  75.  
  76.             nSumRecv += nRecv; 
  77.  
  78.             file.Write(pFileData, nRecvOneTime); 
  79.  
  80.             //显示完成进度,由于文件传递速度快,每隔1000个循环才提示一次接收进度 
  81.  
  82.             if(i%1000 == 0) 
  83.  
  84.             { 
  85.  
  86.                 pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(nSumRecv); 
  87.  
  88.                 itoa(nSumRecv, cRecv, 10); 
  89.  
  90.                 pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)cRecv, (LPARAM)cSize); 
  91.  
  92.             } 
  93.  
  94.         } 
  95.  
  96.         delete []pFileData; 
  97.  
  98.         //最后再显示一次进度 
  99.  
  100.         pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(nSumRecv); 
  101.  
  102.         if(nSumRecv > nSize)      
  103.  
  104.             nSumRecv = nSize; 
  105.  
  106.         itoa(nSumRecv, cRecv, 10); 
  107.  
  108.         pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)cRecv, (LPARAM)cSize);       
  109.  
  110.         if(pClient->m_bEndThreadRecvFile == true
  111.  
  112.             pClient->m_pDlg->MessageBox("已取消传送文件"); 
  113.  
  114.         else 
  115.  
  116.             pClient->m_pDlg->MessageBox("文件传输完毕"); 
  117.  
  118.  
  119.  
  120.         //发送消息,使按钮可用 
  121.  
  122.         pClient->m_pDlg->m_bOnRecvFile = false
  123.  
  124.         pClient->m_pDlg->SendMessage(WM_ENABLEBUTTON); 
  125.  
  126.         file.Close(); 
  127.  
  128.         //提示文本取消..进度条为0 
  129.  
  130.         pClient->m_pDlg->SendMessage(WM_SHOWPROGRESS, (WPARAM)NULL, (LPARAM)cSize); 
  131.  
  132.         pClient->m_pDlg->m_ProgressCtrlRecv.SetPos(0); 
  133.  
  134.     } 
  135.  
  136.     //文件接收完毕,关闭socket,结束线程 
  137.  
  138.     closesocket(pClient->m_socketRealConversationForRecvFile); 
  139.  
  140.     pClient->m_bEndThreadRecvFile = false
  141.  
  142.     AfxEndThread(2); 
  143.  
  144.     return 0; 
  145.  
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;

}

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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值