使用Socket视频监控小demo的实践与分享

个人学习与记录,代码贴的很全,源码链接见文末。

主要实现是在linux端通过连接USB摄像头获取视频数据,传输到windows客户端进行显示。使用v4l2接口获取USB视频帧,将视频帧数据通过socket发送到客户端,客户端使用gdi+技术解析数据并显示到MFC窗口,同时使用select实现多客户端同时连接。

使用V4L2获取视频帧部分详细见V4L2接口的简单使用

然后就是编写socket服务端,并使用select实现多客户端的同时连接,详细代码如下:

linux服务端

socket头文件:

#ifndef MY_SERVER_H
#define MY_SERVER_H

#include <iostream>  
#include<sys/socket.h>  
#include<sys/un.h>  
#include<netinet/in.h>  
#include <string.h>  
#include <sys/types.h>  
#include <unistd.h>  
#include <errno.h>  
#include <arpa/inet.h>  
#include <fcntl.h>  
#include <netdb.h>  
#include <stdio.h>  
#include <map>
#include <netinet/tcp.h> 
#include <pthread.h> 
#include <signal.h>

using namespace std;
#define MAX_COUNT	20
#define BUF_SIZE	100
#define PORT		8888
extern pthread_mutex_t mutex;

enum Type { HEART, OTHER };
typedef struct PACKET_HEAD
{
	Type type;
	int length;
}HEAD;

class CServSocket  
{  
public:  
    CServSocket();  
    virtual ~CServSocket();   
    bool Accept();  
    bool Listen(int port);  
    void Send(char* sendbuf, int len);  
    void Recv(); 
    void  getLocalAddr(char *ip);  
    std::string getCliAddr(int fd);  
    void Close();  
    void InitSelect();
    void ServerSelect();
    bool setNoDelay(bool nodelay); 
    bool setIntOptions(int option, int value); 
private:     
    int sockfd;  
    int connsockfd;  
    struct sockaddr_in servAddr, cliAddr; 
    int client[MAX_COUNT];
    fd_set server_set; 
    int max_fd ;
    map<int,int> mmap;
};

#endif

socket源文件:

#include "Server.h"


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  

CServSocket::CServSocket():max_fd(-1),sockfd(-1),connsockfd(-1)  
{  
	for(int i=0;i<MAX_COUNT;++i)
	{
		client[i] = -1;
	}
}  
  
CServSocket::~CServSocket()  
{  
    Close();
}  
  
void CServSocket::Close()  
{  
	for(int i=0;i<MAX_COUNT;++i)
	{
		if(client[i] >=0)
		{
			::close(client[i]);
		}	
	}
    if(sockfd) 
	{
        ::close(sockfd);  
	}
}  

bool CServSocket::Listen(int port)  //启动并开始监听
{  
    if(port < 0)  
    {  
        std::cout <<"the port err\n";  
        return false;  
    }  
    sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  //启动socket
    if(sockfd < 0)  
    {  
        std::cout << "create socket err\n";  
        return false;  
    }  
    char ip[20]; 
    memset(ip,0,20);
    getLocalAddr(ip);  //获得本地ip地址  
    if(ip)
	{
        servAddr.sin_addr.s_addr = inet_addr(ip);  
	}
    else  
	{
        servAddr.sin_addr.s_addr = htonl(INADDR_ANY);  
	}
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);  

   //setIntOptions(SO_KEEPALIVE, 1);  
	//setNoDelay(true); 
	setIntOptions(SO_REUSEADDR,1);  //打开地址复用
    if(::bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0)  //绑定socket
    {  
        std::cout << "bind err\n";  
        return false;  
    }  
    if(::listen(sockfd, 0) < 0)  //监听
    {  
        std::cout << "listen err\n";  
        return false;         
    }  
    return true;  
} 


void CServSocket::ServerSelect() //执行select阻塞判断
{
	int ret = 0;
	while(1)  
	{
		InitSelect();
		//检测描述符状态
		ret = select(max_fd+1,&server_set,NULL,NULL,NULL);
		if(ret < 0)
		{
			std::cout <<"select error\n";
			break;
		}
		else if(ret == 0)
		{
			std::cout<<"select out_time\n";
			continue;
		}
		else
		{
			if(!Accept())  
			{  
				std::cout << "accept error\n";  
				continue;  
			}    
			Recv();  
		}
	}  
}
void CServSocket::InitSelect()//初始化select
{
	FD_ZERO(&server_set);
	FD_SET(sockfd,&server_set);
	max_fd = max_fd > sockfd ? max_fd : sockfd;
	for(int i=0 ; i<MAX_COUNT ; ++i)
	{
		if(client[i]>=0)
		{
			FD_SET(client[i],&server_set);
			max_fd = max_fd > client[i] ? max_fd : client[i];
		}
	}
}
 bool CServSocket::Accept()  //接受来自Client的请求
{  
		if(FD_ISSET(sockfd,&server_set))
		{
			socklen_t len = sizeof(cliAddr);
			if((connsockfd = ::accept(sockfd, (struct sockaddr*)&cliAddr, &len)) <0)  
			{  
				std::cout<<"accept error\n";
				return false;  
			}  
			int i;
			for(i=0 ; i<MAX_COUNT ; ++i)
			{
				if(client[i] < 0)
				{
				pthread_mutex_lock(&mutex);//多线程互斥锁
					client[i] = connsockfd;
					FD_SET(connsockfd,&server_set);
				pthread_mutex_unlock(&mutex);
					mmap.insert(make_pair(connsockfd,0));//用于保存客户端计数心跳
					max_fd = max_fd > connsockfd ? max_fd : connsockfd;
					break;
				}
			}
			if(i == MAX_COUNT)
			{
				std::cout<<"the number of connections is full\n";
				return false;
			}
			else
			{
				printf("accpet a new client: %s[%d]\n", inet_ntoa(cliAddr.sin_addr) , cliAddr.sin_port);
			}
		}
	
    return true;  
}  
  
bool CServSocket::setIntOptions(int option, int value)  //socket相关信息设定
{  
    bool res = false;  
    if(sockfd)  
    {  
        res = (setsockopt(sockfd, SOL_SOCKET, option, (const void*)&value, sizeof(value)) == 0);  
    }  
    return res;  
} 

bool CServSocket::setNoDelay(bool nodelay)  //设置发送包是否支持延迟
{  
    bool res = false;  
    if(sockfd)  
    {  
        int ndelay = nodelay?1:0;  
        res = (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,(const void*)&ndelay, sizeof(ndelay)) == 0);  
        res =true;        
    }  
    return res;  
}

void  CServSocket::getLocalAddr(char *ip)  //获得本地的ip地址
{    
    char ipaddr[20]={'\0'};  
    const char* shellstr = "ifconfig | sed -n '2p' | awk -F'[ :]+' '{printf $4}'";    
    FILE *fp = popen(shellstr, "r");  //通过管道执行shell命令
    fread(ipaddr, sizeof(char), sizeof(ipaddr), fp);  
    if(ipaddr)  
    {  
        strcpy(ip, ipaddr);  
    }
    pclose(fp);  
    
} 


std::string CServSocket::getCliAddr(int fd)  //获取远端客户机的信息
{  
    char cliip[20];  
    socklen_t size = sizeof(cliAddr);  
    if(getpeername(fd, (sockaddr*)&cliAddr, &size))  //获得客户机的ip地址和端口号
    {  
        strcpy(cliip, "0.0.0.0");  
    }  
    else  
    {  
        sprintf(cliip, "%s[%d]",inet_ntoa(cliAddr.sin_addr),cliAddr.sin_port); 
    }  
    return std::string(cliip);  
} 

void CServSocket::Send(char *sendbuf, int len)  //发送消息
{  
    if(sendbuf==NULL || len < 0) 
	{
		std::cout<<"read stream error\n";
		return;
	}
	for(int i=0;i<MAX_COUNT;++i)
	{
		if(client[i] < 0)
		{
			continue;
		}
		int dataleft = len, total = 0, ret =0;  
		while(total < len)
		{
			pthread_mutex_lock(&mutex);//多线程互斥锁
			ret = ::send(client[i], sendbuf+total, dataleft, 0);
			pthread_mutex_unlock(&mutex);
			if(ret < 0)
			{
				return;
			}
			total += ret;  
			dataleft = len-total; 
		}
		if(total != len)
		{
			printf("client(%d) send error\n",i);
		}
	}
}

void CServSocket::Recv()  //接收来自客户端的消息
{  
	char recvbuf[BUF_SIZE]; 
	int recvlen;
	for(int i=0;i<MAX_COUNT;++i)
	{
		if(client[i] < 0)
		{
			continue;
		}
		if(FD_ISSET(client[i],&server_set))
		{
			HEAD head;
			memset(recvbuf, 0, sizeof(recvbuf));
			recvlen = ::recv(client[i], recvbuf, sizeof(head), 0);
			if(recvlen > 0)
			{
				memset(&head,0,sizeof(head));
				memcpy(&head,recvbuf,sizeof(head));
				if(head.type == HEART)//如果是心跳包,计数归零
				{
					mmap.find(client[i])->second = 0;
					 continue;
				}
				else //如果是数据包,接收显示
				{
					memset(recvbuf, 0, sizeof(recvbuf));
					recvlen = ::recv(client[i], recvbuf, head.length, 0);
					if(recvlen != head.length)
					{
						printf("recv error\n");
					}
					else
					{
						printf("%s:%s\n",getCliAddr(client[i]).c_str(),recvbuf);
					}
					continue;
				}
			}

		}
		else //如果没有来自客户端的消息,心跳计数+1,达到5则认为客户端已死,断开
		{
			mmap.find(client[i])->second +=1;
			if(mmap.find(client[i])->second < 5)
				continue;
		}
		pthread_mutex_lock(&mutex);//多线程互斥锁
		::close(client[i]);
		FD_CLR(client[i],&server_set);
		mmap.erase(mmap.find(client[i]));
		client[i] = -1;
		printf("client(%d) exit!\n",i);
		pthread_mutex_unlock(&mutex);
	}
} 

主函数:

#include "V4l2.h"
#include "Server.h"


void* GrabThread(void*);
V4l2 myV4l2;
CServSocket servSocket; 


int main()
{
	signal(SIGPIPE,SIG_IGN);  //忽略SIGPIPE,防止异常中断进程
	myV4l2.CheckDev();
	myV4l2.InitDev();
	myV4l2.StartCapture();
	pthread_t ptr;
	if(pthread_create(&ptr,NULL,GrabThread,NULL) != 0)//创建读取视频帧线程
	{
		std::cout<<"create video thread failed!\n";
		exit(1);
	}

    if(!servSocket.Listen(PORT))//启动socket并监听端口
	{
		std::cout<<"socket start failed!\n";
		exit(1);
	}
	std::cout<<"dev init complete, waiting for connect...\n";
	
	servSocket.ServerSelect(); //执行select
	
	return 0;
}
void* GrabThread(void* arg)
{
	int length;
    char img_buf[BUFFER_SIZE];
	while(1)
	{
		length = 0;
		bzero(img_buf, BUFFER_SIZE);  
		myV4l2.ReadStream(img_buf,&length); //读取视频数据
		
		//发送数据
		servSocket.Send((char*)&length,sizeof(int));
		servSocket.Send(img_buf,sizeof(char)*length);
	} 
	return NULL;
}

Windows客户端

MFC对话框头文件:

// ClientDlg.h: 头文件
//

#pragma once
#include "afxcmn.h"


// CClientDlg 对话框
class CClientDlg : public CDialog
{
// 构造
public:
	CClientDlg(CWnd* pParent = NULL);	// 标准构造函数
	~CClientDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_CLIENT_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

#define  BUFFER_SIZE 1000000
// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()

private:
	typedef struct DATA
	{
		int length;
		char RecvBuffer[BUFFER_SIZE];
	}IMG_DATA;
	enum Type { HEART, OTHER };
	typedef struct PACKET_HEAD
	{
		Type type;
		int length;
	}HEAD;
	IMG_DATA img_data; //图像数据
	CIPAddressCtrl ip_address;//IP地址变量
	CRect m_rect; //记录对话框位置
	BOOL con_flag = FALSE; //标记按钮状态
	SOCKET ClientSocket; //声明socket
	CWinThread *plotThread = NULL; //声明线程
	static UINT PlotImageThread(LPVOID param);//声明线程函数
	CWinThread *heartThread = NULL; //声明线程
	static UINT HeartBeatThread(LPVOID param);//声明线程函数
	CRITICAL_SECTION g_cs;//临界变量
public:
	afx_msg void OnBnClickedConnect();
	afx_msg void OnSize(UINT nType, int cx, int cy);
	BOOL ConnectSocket();
	BOOL SendMsg(const char *msg);
	BOOL ReceiveImg();
	int Recv(char *recv_buf, int len);
	BOOL DrawImage();

	virtual void OnCancel();
	virtual void OnOK();
	afx_msg void OnClose();
};

MFC对话框源文件:

// ClientDlg.cpp: 实现文件
//

#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#include "afxdialogex.h"
#include "MoveDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CClientDlg 对话框



CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
	: CDialog(IDD_CLIENT_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	InitializeCriticalSection(&g_cs);//初始化临界区
}

CClientDlg::~CClientDlg()
{
	DeleteCriticalSection(&g_cs);//删除临界区
}

void CClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_IPADDRESS1, ip_address);
}

BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_CONNECT, &CClientDlg::OnBnClickedConnect)
	ON_WM_SIZE()
	ON_WM_CLOSE()
END_MESSAGE_MAP()


// CClientDlg 消息处理程序

BOOL CClientDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	GetClientRect(&m_rect); //获取对话框的大小
	const char * strIP = "192.168.3.71";
	DWORD dwIP;
	inet_pton(AF_INET, strIP, &dwIP);
	ip_address.SetAddress(ntohl(dwIP));
	SetDlgItemTextW(IDC_EDIT1, _T("8888"));


	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CClientDlg::OnBnClickedConnect()
{
	// TODO: 在此添加控件通知处理程序代码
	if (!con_flag)
	{	
		if (!ConnectSocket())
		{
			MessageBox(_T("Socket连接出错!"));
			return;
		}
		
		if (!SendMsg("hello"))
		{
			MessageBox(_T("发送消息出错!"));
			return;
		}
		plotThread = ::AfxBeginThread(PlotImageThread, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);//绘图线程
		if (plotThread == NULL)
		{
			MessageBox(_T("线程创建失败!"));
			return;
		}
		heartThread = ::AfxBeginThread(HeartBeatThread, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);//心跳线程
		if (heartThread == NULL)
		{
			MessageBox(_T("线程创建失败!"));
			return;
		}
		con_flag = TRUE;
		GetDlgItem(IDC_CONNECT)->SetWindowTextW(_T("断开连接"));	
	}
	else
	{
		::shutdown(ClientSocket, SD_SEND);
		WaitForSingleObject(plotThread->m_hThread, INFINITE);//等待绘图线程结束

		EnterCriticalSection(&g_cs);//防止心跳线程不知道已关闭socket
		::closesocket(ClientSocket);
		ClientSocket = NULL;
		LeaveCriticalSection(&g_cs);
		GetDlgItem(IDC_CONNECT)->EnableWindow(FALSE);
		GetDlgItem(IDC_CONNECT)->SetWindowTextW(_T("正在断开,请稍等"));
		WaitForSingleObject(heartThread->m_hThread, INFINITE);//等待心跳线程结束
		
		con_flag = FALSE;
		GetDlgItem(IDC_CONNECT)->SetWindowTextW(_T("连接"));
		GetDlgItem(IDC_CONNECT)->EnableWindow(TRUE);
		Invalidate();
	}

}


void CClientDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码
	Invalidate(); 
	MoveDlg::OnSize(this, m_rect, nType, cx, cy);
}

BOOL CClientDlg::ConnectSocket()
{
	/* Create Socket */
	ClientSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ClientSocket == INVALID_SOCKET)
	{
		MessageBox(_T("Socket创建失败!"));
		return FALSE;
	}
	struct sockaddr_in ClientAddr;
	ClientAddr.sin_family = AF_INET;
	BYTE f1, f2, f3, f4;
	ip_address.GetAddress(f1, f2, f3, f4);
	char ip[20];
	sprintf_s(ip, 20, "%d.%d.%d.%d", f1, f2, f3, f4);
	inet_pton(AF_INET, ip, &ClientAddr.sin_addr);
	CString port;
	GetDlgItem(IDC_EDIT1)->GetWindowTextW(port);
	ClientAddr.sin_port = htons(_ttoi(port));
	memset(ClientAddr.sin_zero, 0x00, 8);
	/* connect socket */
	if (::connect(ClientSocket, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)
	{
		MessageBox(_T("请求连接出错!"));
		return FALSE;
	}

	return TRUE;
}

BOOL CClientDlg::SendMsg(const char * msg)
{
	HEAD head;
	char buf[10];
	head.type = OTHER;
	head.length = (int)strlen(msg);
	memset(buf, 0, 10);
	memcpy(buf, &head, sizeof(head));
	//MessageBox(_T("测试1!"));
	EnterCriticalSection(&g_cs);//锁住连续发两个包,防止插入心跳包,对端接收混乱
	//MessageBox(_T("测试2!"));
	if (::send(ClientSocket, buf, sizeof(head), 0) == SOCKET_ERROR)
	{
		return FALSE;
	}
	if (::send(ClientSocket, msg, head.length, 0) == SOCKET_ERROR)
	{
		return FALSE;
	}
	LeaveCriticalSection(&g_cs);
	return TRUE;
}

BOOL CClientDlg::ReceiveImg()
{
	int iResult = 0;
	int lenReaded = 0;
	int lenLeaved = 0;
	img_data.length = 0;
	iResult = Recv((char*)&img_data.length, sizeof(int));
	if (iResult <= 0) 
	{
		return FALSE;
	}

	memset(img_data.RecvBuffer, 0, BUFFER_SIZE);
	lenLeaved = img_data.length;

	while (lenReaded < img_data.length)
	{
		iResult = Recv(img_data.RecvBuffer + lenReaded, lenLeaved);
		if (iResult <= 0)
		{
			return FALSE;
		}
		lenReaded += iResult;
		lenLeaved = img_data.length - lenReaded;
	}
	return TRUE;
}

int CClientDlg::Recv(char * recv_buf, int len)
{
	if (ClientSocket == INVALID_SOCKET || recv_buf == NULL)
	{
		return -1;
	}
	return ::recv(ClientSocket, recv_buf, len, 0);
}

BOOL CClientDlg::DrawImage()
{
	if (img_data.length <= 0)
	{
		return FALSE;
	}
	HGLOBAL hImageData = ::GlobalAlloc(GMEM_MOVEABLE, img_data.length);//创建全局内存对象
	if (hImageData == NULL)
	{
		return FALSE;
	}
	BYTE* pImgData = (BYTE*)::GlobalLock(hImageData);
	if (pImgData == NULL)
	{
		::GlobalFree(hImageData);
		return FALSE;
	}
	::memcpy(pImgData, img_data.RecvBuffer, img_data.length);//进行内存拷贝
	::GlobalUnlock(hImageData);
	IStream *pStmImg = NULL;
	if (::CreateStreamOnHGlobal(hImageData, FALSE, &pStmImg) != S_OK)//创建数据流
	{
		pStmImg->Release();
		::GlobalFree(hImageData);
		return FALSE;
	}
	Image *image = Image::FromStream(pStmImg);//通过流获得图像对象
	pStmImg->Release();

	CWnd* pwnd = GetDlgItem(IDC_DISPLAY);//IDC_DISPLAY 为图像控件的 ID 
	CDC* dc = pwnd->GetDC(); //获取图像控件的设备上下文 
	CRect rect;
	pwnd->GetClientRect(&rect); //获取客户区域的信息 
	Graphics graph(dc->GetSafeHdc());
	graph.DrawImage(image, 0, 0, rect.Width(), rect.Height()); //在指定图像控件的区域中绘制图像 
	ReleaseDC(dc); //释放资源 
	::GlobalFree(hImageData);
	return TRUE;
}

UINT CClientDlg::PlotImageThread(LPVOID param)//绘图线程
{
	CClientDlg *myDlg = (CClientDlg*)param;
	myDlg->Invalidate();
	while (1)
	{
		if (!myDlg->ReceiveImg())
		{
			break;
		}
		if (!myDlg->DrawImage())
		{
			break;
		}
	}
	return 0;
}

UINT CClientDlg::HeartBeatThread(LPVOID param)//心跳线程
{
	CClientDlg *myDlg = (CClientDlg*)param;
	HEAD head;
	char buf[10];
	while (1)
	{
		head.type = HEART;
		head.length = 0;
		memset(buf, 0, 10);
		memcpy(buf, &head, sizeof(head));
		EnterCriticalSection(&myDlg->g_cs);
		if (myDlg->ClientSocket == NULL)
		{
			break;
		}
		if (::send(myDlg->ClientSocket, buf, sizeof(head), 0) == SOCKET_ERROR)
		{
			myDlg->MessageBox(_T("发送心跳包出错!"));
			break;
		}
		LeaveCriticalSection(&myDlg->g_cs);
		Sleep(3000);
	}
	LeaveCriticalSection(&myDlg->g_cs);
	return 0;
}


void CClientDlg::OnClose()
{
	CDialog::OnCancel();
}
void CClientDlg::OnCancel(){}
void CClientDlg::OnOK(){}

MFC窗口大小拖动头文件:

#pragma once //MoveDlg.h
class MoveDlg
{
public:
	MoveDlg();
	~MoveDlg();
	static void OnSize(const CDialog *dlg, CRect &rect, UINT nType, int cx, int cy);
	static void ChangeSize(const CDialog *dlg, CRect &rect, UINT nID, int x, int y);
};

MFC窗口大小拖动源文件:

#include "stdafx.h"
#include "MoveDlg.h"

MoveDlg::MoveDlg(){}
MoveDlg::~MoveDlg(){}
void MoveDlg::OnSize(const CDialog * dlg, CRect &rect, UINT nType, int cx, int cy)
{
	if (nType != SIZE_MINIMIZED) //判断窗口是不是最小化了,因为窗口最小化之后 ,窗口的长和宽会变成0,当前一次变化的时就会出现除以0的错误操作
	{
		CWnd* pChildWnd = dlg->GetWindow(GW_CHILD);
		UINT nCtrlID = 0;
		while (pChildWnd != NULL) //遍历对每个控件做改变
		{
			nCtrlID = pChildWnd->GetDlgCtrlID();
			ChangeSize(dlg, rect, nCtrlID, cx, cy);
			pChildWnd = pChildWnd->GetWindow(GW_HWNDNEXT);
		}

		dlg->GetClientRect(&rect); //最后要更新对话框的大小,当做下一次变化的旧坐标
	}
}

void MoveDlg::ChangeSize(const CDialog *dlg, CRect &m_rect, UINT nID, int x, int y)//nID为控件ID,x, y分别为对话框的当前长和宽
{
	CWnd *pWnd = dlg->GetDlgItem(nID);
	if (pWnd != NULL) //判断是否为空,因为在窗口创建的时候也会调用OnSize函数,但是此时各个控件还没有创建,Pwnd为空
	{
		CRect rec;
		pWnd->GetWindowRect(&rec); //获取控件变化前的大小
		dlg->ScreenToClient(&rec); //将控件大小装换位在对话框中的区域坐标
		rec.left = rec.left*x / m_rect.Width(); //按照比例调整空间的新位置
		rec.top = rec.top*y / m_rect.Height();
		rec.bottom = rec.bottom*y / m_rect.Height();
		rec.right = rec.right*x / m_rect.Width();
		pWnd->MoveWindow(rec); //伸缩控件
	}
}

全部代码下载链接:全部源码

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值