前言:本例写于2012年百度空间,现在百度空间没了,ishare也找不到了。而且我记得当时这个例子是有问题的,有bug没找出来,监控几十秒后画面就不动了。不算一个好例子,问题多多。
只能说再发一下,具有一些参考意义吧。只做参考使用。
本例不假设有任何意外情况,这个小程序当然不能算完善的。。比如程序关闭了,就关闭了,没有去响应WM_CLOSE消息,然后在里面结束线程。还有服务端窗口等待时透明。。这些琐碎的事,自己去完成吧。。。。
我只是完成关键部分,还是那句话,有兴趣的朋友,可以自己去完善。。比如,地址不对,弹出一个消息提示框。或者服务端关闭了。。。
下工程的话,记住安装GDI+包。不然,生成Release版,倒不需要什么附带的文件,大部分系统版本应该都有gdiplus.dll这个动态链接库吧。
问题:
在自己电脑上,倒没什么问题。(本机测试,IP地址填127.0.0.1就行了)
可一到别人电脑上,只可以坚持几十秒钟,就出问题了,难道是数据丢失了,对不上了。如果是这个的话,那就算了,我以后做一个TCP的,看是什么样的。。如果不是的话,那也算了,因为我估计,可能花很久时间我也找不出来,只能是以后对某些知识点有更深入的了解再来找了。现在,我也不急于解决这个问题。
注意:
如果是用VC编译器打开工程,然后再运行,那就不用注意这个事了。如果是直接点击应用程序运行的话,那么请运行Release版的程序,因为我生成的是Release版的,Debug版是以前的,会有错,客户端和服务端都得Release版。切记。因为之前我就犯了这个错误。我还找了半天。
这个例子所涉及的知识点,我以前都讲过,现在只是把它们组合在一起,如果看不懂的话,按照下面顺序学习。。。这里,我就简短地提一下这个过程。不做详细说明了。
最好先看一下:
屏幕截图,第八十七和第八十八个函数。
GDI+入门,这里可以找到相关的知识点,如在内存中转换图片格式。
TCP&UDP协议,只有代码,没多少说明,不懂的可到网上另找相关资料。UDP的例子,在复制代码的时候,不知怎么,有些空格被清除了。再直接复制的话,会出错。需手动添加空格。
客户端编写的大概过程:
1.包含相关头文件,连接动态链接库,初始化GDI+
3.给对话框类添加一个成员变量 CLSID m_encoderClsid;
3.给对话框添加一个按钮控件,关联变量m_Connect,再添加一个IP地址输入框控件CIPAddressCtrl,关联变量m_IPAddress
4.添加按钮单击消息处理函数,该函数代码如下:
void CMonClientDlg::OnButton1()
{
// TODO: Add your control notification handler code here
m_Connect.EnableWindow(FALSE);
//连接,一切交给线程函数去执行
CreateThread(NULL,0,ThreadProc,this,0,NULL);
}
5.编写线程函数,函数代码如下:
DWORD _stdcall ThreadProc(LPVOID lpParameter)//线程执行函数
{
//获取IP地址
CMonClientDlg *pClientDlg=(CMonClientDlg *)lpParameter;
CString strIPAddress;
pClientDlg->m_IPAddress.GetWindowText(strIPAddress);
struct sockaddr_in client,server;
SOCKET s;
//初始化winsock
WSADATA wsaData;
WSAStartup(0x0202,&wsaData);
//建立套接字
s=socket(AF_INET,SOCK_DGRAM,0);
//初始化客户端地址结构
memset((char *)&client,0,sizeof(client));
client.sin_family=AF_INET;
client.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
client.sin_port=htons(0);
//绑定socket对象到客户地址
bind(s,(struct sockaddr *)&client,sizeof(client));
//初始化服务器地址结构,确定要连接的服务器
memset((char *)&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.S_un.S_addr=inet_addr(strIPAddress);
server.sin_port=htons(39872);
HWND DeskWnd=::GetDesktopWindow();//获取桌面窗口句柄
RECT DeskRC;
::GetClientRect(DeskWnd,&DeskRC);//获取桌面窗口大小
HDC DeskDC=GetDC(DeskWnd);
//兼容位图
HBITMAP hDeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);
HDC memDC;
//兼容DC
memDC=::CreateCompatibleDC(DeskDC);
SelectObject(memDC,hDeskBmp);
//无限循环,不停地的捕获桌面图像,发送数据
while(TRUE)
{
::BitBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,SRCCOPY);
Bitmap bmp(hDeskBmp,NULL);
//创建流
IStream *pStream;
::CreateStreamOnHGlobal(NULL,TRUE,&pStream);
//以JPEG图片格式储存数据到流中
bmp.Save(pStream,&pClientDlg->m_encoderClsid);
//获得与流对应的内存句柄
HGLOBAL hMem;
::GetHGlobalFromStream(pStream,&hMem);
DWORD dwSize=::GlobalSize(hMem);//获得内存块大小
BYTE *pImgData=(BYTE *)GlobalLock(hMem);//获得内存块首地址*/
//发送数据大小,告诉服务端有多少数据要发送
sendto(s,(char *)&dwSize,sizeof(DWORD),0,(struct sockaddr *)&server,sizeof(server));
DWORD sendSize=60000;//每次发送的数据大小
while(TRUE)
{
if(dwSize>sendSize)
sendto(s,(char *)pImgData,sendSize,0,(struct sockaddr *)&server, sizeof(server));
else
{
sendto(s,(char *)pImgData,dwSize,0,(struct sockaddr *)&server,sizeof(server));
break;
}
pImgData+=sendSize;
dwSize-=sendSize;
}
pStream->Release();//释放资源
//等候通知再一次发送数据
char buf[4];
int serLen=sizeof(server);
recvfrom(s,buf,4,0,(struct sockaddr *)&server,&serLen);
}
}
6.编写GetEncoderClsid函数
//获取图片格式的CLSID自定义函数
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num= 0;
UINT size= 0;
ImageCodecInfo* pImageCodecInfo= NULL;
GetImageEncodersSize(&num, &size);
if(size== 0)
{
return -1;
}
pImageCodecInfo= (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo== NULL)
{
return -1;
}
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j=0; j< num; ++j)
{
if(wcscmp(pImageCodecInfo[j].MimeType, format)== 0)
{
*pClsid= pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
7.在对话框类构造函数获取CLSID,构造函数代码如下:
CMonClientDlg::CMonClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMonClientDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMonClientDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//初始化GDI+
GdiplusStartup(&m_pGdiToken,&m_gdiplusStartupInput,NULL);
//获取JPEG的CLSID
GetEncoderClsid(L"image/jpeg",&m_encoderClsid);
}
服务端编写的大概过程
1.初始化GDI+
2.对话框类添加如下变量
Image *m_pImg;//保存接收过来的图片
int cliWidth,cliHeight;//窗口的大小
3.在对应框初始化函数里创建一个线程,代码如下:
this->MoveWindow(0,0,800,600);
//等待连接,一切交给线程函数去执行
CreateThread(NULL,0,ThreadProc,this,0,NULL);
4.编写线程函数,函数代码如下:
DWORD _stdcall ThreadProc(LPVOID lpParameter)//线程执行函数
{
CMonServerDlg *pSerDlg=(CMonServerDlg *)lpParameter;
struct sockaddr_in client,server;
SOCKET s;
//初始化winsock
WSADATA wsaData;
WSAStartup(0x0202,&wsaData);
//建立套接字
s=socket(AF_INET,SOCK_DGRAM,0);
//初始化服务器地址结构
memset((char *)&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
server.sin_port=htons(39872);
//绑定socket对象到服务器地址
bind(s,(struct sockaddr *)&server,sizeof(server));
int cliLen=sizeof(client);
DWORD dwSize;
//无限循环,接收数据,绘制图片
while(TRUE)
{
//接收数据的大小
recvfrom(s,(char *)&dwSize,sizeof(DWORD),0,(struct sockaddr *)&client,&cliLen);
//分配内存
HGLOBAL hMem=::GlobalAlloc(GMEM_MOVEABLE,dwSize);
//创建流
IStream *pStream;
CreateStreamOnHGlobal(hMem,TRUE,&pStream);
//获得首地址
BYTE *pImgData=(BYTE *)GlobalLock(hMem);
DWORD recSize=60000;//每次接收的数据大小
while(TRUE)
{
if(dwSize>recSize)
recvfrom(s,(char *)pImgData,recSize,0,(struct sockaddr *)&client,&cliLen);
else
{
recvfrom(s,(char *)pImgData,dwSize,0,(struct sockaddr *)&client,&cliLen);
break;
}
pImgData+=recSize;
dwSize-=recSize;
}
if(pSerDlg->m_pImg!=NULL)
{
delete pSerDlg->m_pImg;
pSerDlg->m_pImg=NULL;
}
pSerDlg->m_pImg=Image::FromStream(pStream);
pStream->Release();
pSerDlg->Invalidate();
Sleep(300);//延迟三百毫秒传输,暂时这样了。。。
//发送消息给客户端,发送图片过来。
sendto(s,"yes",4,0,(struct sockaddr *)&client,cliLen);
}
}
5.对话框OnPaint函数,绘制图片,else里代码如下:
else
{
CDialog::OnPaint();
CDC *pDC=GetDC();
if(m_pImg!=NULL)
{
Graphics graphics(pDC->m_hDC);
graphics.DrawImage(m_pImg,0,0,cliWidth,cliHeight);
}
// pDC->Rectangle(0,0,300,300);
}
似乎就这些了,反正有完整的工程,可以直接到工程里看,需要手打的代码估计不会超过一百行,还觉得难么。
关键在线程函数里的代码,把那些看懂就差不多了。
工程下载地址:
客户端:_下载 - 爱问文库
服务端:_下载 - 爱问文库
再说一句,运行Release版下的程序。没办法了,已经上传上去了。