这学期什么都没学,一直在钻研VC,平时上课,老师在上面讲着,我就躲到后排,耳朵里赛个MP3,埋头看我的VC..马上就要开始考试了,居然也不紧张,看来自己已经成老油条了,也不怕什么挂科了(又不是没挂过,所以不在乎了)..
好了,费话不多说,我就说说我是怎么做这个程序的吧..其实这也谈不上是什么远程监控,好的远程监控软件,自己到网上看看报价就知道了,所以,我这个只能叫程序,不能称为软件...
这个程序共分为两部分:服务端(Server)和客户端(Client),程序能完成下面三大功能:
1.监视服务端主机的桌面,并传输给客户端显示.
2.录制服务端的声音,发送给客户端播放..
3.文件传输,能在客户端显示服务端的主机目录,类似windows资源管理器,并能进行文件传输.
当初在设计时,是想这个程序还能完成控制,就如同QQ的远程协助,可惜在调试程序时很麻烦,因为调试程序时,服务端和客户端都在自己机子上,不好看效果..所以为了省事,就跳过了这部分的功能,但这部分应该也不难,主要是两个API函数,模拟鼠标和按键功能,一个是VOID mouse_event(),一个是VOID keybd_event(),具体的使用你可以查看MSDN..有兴趣的朋友可以添加上这个功能,还可以加上别的功能,比如远程关机,注销等..下面我就按照上面的顺序,讲下如何实现这三大功能吧,程序的源码我已经传到优快云的资源区,需要的朋友可以下来看看..http://download.youkuaiyun.com/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; //要传输的文件路径
- };
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;
- }
//线程函数,用于传送主机桌面画面
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;
- }
//发送屏幕画面函数
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);
- }
//捕获屏幕位图信息和数据
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;
- }
//线程函数,用于接收主机屏幕画面
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;
- }
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(); //释放输入音频缓冲
- };
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();
- }
//当音频输入缓冲录满时,释放缓冲
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 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 ;
- }
- }
//当一内存块被录满时,调用此函数,为下次录音准备
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;
- }
//线程函数,用于接收主机声音
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 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;
- }
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;
}
到现在,这个程序基本完成了,我只说了重要的部分,其实,我这程序还有些问题没解决,比如,传送文件过程,文件传过来了,可是有些文件很数据丢失,文件打不开等错误..由于得法是自己想的,所以不知如何解决,希望有经验的朋友能告诉我,在此,先谢谢你了..!!