MFC基于对话框框架的简易飞鸽系统(四)—-群发信息

  • Post author:
  • Post category:其他


实现过程第一篇已经写了,这里就是具体的代码实现过程了。先看一下群发信息的界面:

群发信息

GroupChatDlg.h

#pragma once
#include "afxwin.h"
#include "afxcmn.h"
#include <WinSock2.h>  
#include <stdio.h>  
#include <iostream>   

using namespace std;

#pragma comment(lib, "ws2_32.lib")   

#define GROUP_PORT 12822  

#define WM_UPDATE_GROUP (WM_USER + 100) 



// GroupChatDlg 对话框

class GroupChatDlg : public CDialog
{
	DECLARE_DYNAMIC(GroupChatDlg)

public:
	GroupChatDlg(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~GroupChatDlg();

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

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
	virtual BOOL OnInitDialog();
	DECLARE_MESSAGE_MAP()
public:
	CStatic group_name;  //显示群的名字
	CListBox group_infList; // 显示群成员发送的消息列表
	CEdit group_Msg;   // 显示在群里发送的消息

	CWinThread * recvThread; // 接收普通udp信息的线程
	CFont cfont2; // 自定义的字体
	CString group_names; // 所有的群成员名字用一个CString来表示,中间用!隔开
	CString group_ips; // 所有的群成员的ip用一个CString来表示,中间用!隔开
	//vector<CString> names; // 将所有的成员名解析到一个vector中
	//vector<CString> ips; // 将所有成员的ip解析到一个vector中
	static UINT RecvUDPThread(LPVOID pParam); 	// 自定义的接收普通udp信息的线程
	afx_msg LRESULT OnUpdateGroup(WPARAM wParam, LPARAM lParam); // 处理接收到的自定义消息
	afx_msg void OnBnClickedSendgMsg();
};

GroupChatDlg.cpp

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

#include "stdafx.h"
#include "FeiGe.h"
#include "GroupChatDlg.h"
#include "afxdialogex.h"
#include <vector>
#include "string"
#include <tchar.h>

vector<CString>   names; // 存放群内所有主机名
vector<CString>   ips; // 存放群里所有主机ip
static CListCtrl group_memberList;// 显示群成员信息列表
// GroupChatDlg 对话框

IMPLEMENT_DYNAMIC(GroupChatDlg, CDialog)

GroupChatDlg::GroupChatDlg(CWnd* pParent /*=NULL*/)
	: CDialog(IDD_DIALOG1, pParent)
{

}

GroupChatDlg::~GroupChatDlg()
{
}

void GroupChatDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_GROUP_NAME, group_name);
	DDX_Control(pDX, IDC_GINF_LIST, group_infList);
	DDX_Control(pDX, IDC_EDITG_MSG, group_Msg);
	DDX_Control(pDX, IDC_GROUP_LIST, group_memberList);
}


BEGIN_MESSAGE_MAP(GroupChatDlg, CDialog)
	ON_BN_CLICKED(IDC_SENDG_MSG, &GroupChatDlg::OnBnClickedSendgMsg)
	ON_MESSAGE(WM_UPDATE_GROUP, &GroupChatDlg::OnUpdateGroup)
END_MESSAGE_MAP()
// 分割字符串 
// 将结果以一个vector<CString>形式返回
vector<CString> split3(CString strSource, CString ch) {
	vector<CString> result;
	int iPos = 0;
	CString strTmp;
	strTmp = strSource.Tokenize(ch, iPos);
	while (strTmp.Trim() != _T(""))
	{
		result.push_back(strTmp);
		strTmp = strSource.Tokenize(ch, iPos);
	}
	return result;
}


// 获取本地主机的主机名
CString GetMyName()
{
	//1.初始化wsa  
	WSADATA wsaData;
	int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
	//2.获取主机名  
	char hostname[256];
	ret = gethostname(hostname, sizeof(hostname));
	if (ret == SOCKET_ERROR)
	{
		return false;
	}
	::WSACleanup();
	return hostname;
}
// 接受自定义的消息,并更新控件
LRESULT GroupChatDlg::OnUpdateGroup(WPARAM wParam, LPARAM lParam)
{
	CString inf;
	inf = *((CString*)wParam);

	if (split3(inf, "!").size() == 2)
	{
		// 如果是想要发送文本信息
		// 将要发送的字符串以一定格式输出
		CString myText;
		myText += split3(inf, "!")[0];
		myText += " :(";
		CTime m_time;
		CString time;
		m_time = CTime::GetCurrentTime();             //获取当前时间日期  
		time = m_time.Format(_T("%Y-%m-%d %H:%M:%S %A"));   //格式化日期时间  
		myText += time;
		myText += ")";
		myText += "\r\n";
		int length = group_infList.GetCount();
		group_infList.InsertString(length, myText);
		CString content = "		  ";
		content += split3(inf, "!")[1];
		int length2 = group_infList.GetCount();
		group_infList.InsertString(length2, content);
	}

	return 0;
}
// 接受UDP信息线程
UINT GroupChatDlg::RecvUDPThread(LPVOID pParam)
{
	GroupChatDlg *pDlg = (GroupChatDlg *)pParam;
	//初始化网络环境  
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
	{
		AfxMessageBox("WSAStartup failed\n", MB_OK, 0);
		return -1;
	}

	//建立一个UDP的socket  
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == SOCKET_ERROR)
	{

		AfxMessageBox("create socket failed\n", MB_OK, 0);
		return -1;
	}

	//绑定地址信息  
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(GROUP_PORT);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	bind(sock, (sockaddr*)&serverAddr, sizeof(sockaddr));
	char buf[512];
	while (true)
	{
		memset(buf, 0, 512);
		// 网络节点的信息,用来保存客户端的网络信息  
		sockaddr_in clientAddr;

		memset(&clientAddr, 0, sizeof(sockaddr_in));

		int clientAddrLen = sizeof(sockaddr);
		//接收客户端发来的数据  
		int ret = recvfrom(sock, buf, 512, 0, (sockaddr*)&clientAddr, &clientAddrLen);
		CString inf = buf;
		if (strlen(inf) >= 1)
		{

			// 发送消息通知主线程更新控件内容
			::PostMessage(pDlg->m_hWnd, WM_UPDATE_GROUP, (WPARAM)(&inf), 0);
		}
		Sleep(1000);
	}
	return 0;
}
// 初始化群成员对话框
BOOL GroupChatDlg::OnInitDialog()
{

	CDialog::OnInitDialog();
	
	// TODO: 在此添加额外的初始化代码
	// 初始化表格
	group_memberList.ModifyStyle(0, LVS_REPORT);               // 报表模式   
	group_memberList.SetExtendedStyle(group_memberList.GetExtendedStyle() | LVS_EX_GRIDLINES ); 	// 间隔线
																				// 插入表头
	group_memberList.InsertColumn(0, "群成员");
	group_memberList.InsertColumn(1, "IP");
	CRect rect;
	group_memberList.GetClientRect(rect); //获得当前客户区信息   
	group_memberList.SetColumnWidth(0, rect.Width()/2); //设置列的宽度。 
	group_memberList.SetColumnWidth(1, rect.Width() / 2); //设置列的宽度。

	// 设置对话群的名称字体
	cfont2.CreatePointFont(150, _T("黑体"), NULL);
	GetDlgItem(IDC_GROUP_NAME)->SetFont(&cfont2);
	group_name.SetWindowTextA("简易飞鸽开发交流群");
	
	// 初始化群成员信息列表
	  names = split3(group_names, "!");
	  ips = split3(group_ips, "!");
	for (int i = 0; i < names.size(); i++)
	{
		group_memberList.InsertItem(i,names[i]);
		group_memberList.SetItemText(i,1,ips[i]);
	}
	
	// 启动接收普通信息的线程
	recvThread = AfxBeginThread((AFX_THREADPROC)RecvUDPThread, this);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 群发消息
void GroupChatDlg::OnBnClickedSendgMsg()
{
	// TODO: 在此添加控件通知处理程序代码
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	// 启动socket api   
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		AfxMessageBox("不支持2.2版本", MB_OK);
	}
	// 创建并绑定udp套接字
	//建立一个UDP的socket  
	SOCKET udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);


	// 将要发送的字符串以一定格式输出
	CString sendText, edit_text;
	// 给发送的信息之前加上主机名
	CString host_name = GetMyName();
	sendText += host_name;
	sendText += "!";
	
	GetDlgItem(IDC_EDITG_MSG)->GetWindowText(edit_text);
	// 将自己发的消息添加到消息列表中
	sendText += edit_text;
	// 发送文本信息sd
	// 添加一个“回车换行”
	// 注意,添加它并不是必须的,但是如果使用本软件作为客户端调试网络协议,
	// 比如SMTP、FTP等,就要添加它了。因为这些协议都要求使用“回车换行”作为一个命令的结束标记
	sendText += "\r\n";
	// 通过遍历ips中的所有ip地址,将消息发给每一个人
	for (int i = 0; i < ips.size(); i++)
	{
		// 填写要发送信息的地址
		sockaddr_in toAddr;
		toAddr.sin_family = AF_INET;
		toAddr.sin_port = htons(GROUP_PORT);
		toAddr.sin_addr.S_un.S_addr = inet_addr(ips[i]);
		int length = sendto(udp_socket, sendText, strlen(sendText), 0, (SOCKADDR*)&toAddr, sizeof(SOCKADDR));
		if (SOCKET_ERROR == length)
		{
			AfxMessageBox("发送UDP信息错误", MB_OK);
		}
	}
	// 清空文本编辑框
	group_Msg.SetWindowTextA("");
	closesocket(udp_socket);
	::WSACleanup();
}



版权声明:本文为MarhongBaLa原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。