基于MFC的模拟网络聊天窗口程序

tech2022-08-07  137

概要:基于多线程和sicket通信 实现数据的收发显示,模拟聊天窗口

大概流程:

步骤:

1、制作聊天界面

2、MFC 的套接字库: AfxSocketInit   用于进行版本协商

BOOL AfxSocketInit( WSADATA* lpwsaData = NULL

);

在程序初始化是调用 该函数,判断套接字库协商是否成功,如果失败,终止程序运行    所需头文件:Afxsock.h

3、初始化套接字函数:InitSocket

4、为了接收窗口始终在运行接收数据,所以单开一个线程,用来接收数据

     在创建线程的时候需要传递俩个参数:1、创建的套接字变量  2、对话框句柄

     为方便传参,定义结构体

5、编写接收函数:

     1、接收通过线程传递的参数:socket 、 窗口句柄

     2、while循环实现 接收功能

     3、将接收到的信息通过消息响应函数,在接收编辑框中显示

6、编写发送函数

      UDP 发送

 

代码:

// ChatDlg.cpp: 实现文件 // #include "pch.h" #include "framework.h" #include "Chat.h" #include "ChatDlg.h" #include "afxdialogex.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() // CChatDlg 对话框 CChatDlg::CChatDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_CHAT_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } CChatDlg::~CChatDlg() { closesocket(m_socket); } void CChatDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CChatDlg, CDialogEx) /*ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON()*/ // 添加消息映射 ON_MESSAGE(WM_RECVDATA, OnrecvDate) ON_BN_CLICKED(IDC_BTN_SEND, &CChatDlg::OnBnClickedBtnSend) END_MESSAGE_MAP() // CChatDlg 消息处理程序 BOOL CChatDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { 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: 在此添加额外的初始化代码 if (!AfxSocketInit()) { MessageBox(_T("套接字库协商加载失败!")); return false; } InitSocket(); recvPARAM* pRecvParam = new recvPARAM; // 创建结构体对象 // 初始化结构体内变量 pRecvParam->recvSock = m_socket; pRecvParam->recvHandle = m_hWnd; // 这里传递的是对话框的句柄。 也可以传递编辑框句柄 // 创建线程 HANDLE hThread; hThread = CreateThread(NULL,0,RecvPro,(LPVOID)pRecvParam,0,NULL); CloseHandle(hThread); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CChatDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CChatDlg::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 { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CChatDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } bool CChatDlg::InitSocket() { // 创建套接字 m_socket = socket(AF_INET,SOCK_DGRAM,0); // 判断套接字是否创建成功 if (INVALID_SOCKET == m_socket) { MessageBox(_T("套接字创建失败!")); return false; } // 绑定端口 // 定义地址结构体变量 SOCKADDR_IN addrSock; //inet_pton(AF_INET,"",&addrSock.sin_addr.S_un.S_addr); addrSock.sin_family = AF_INET; addrSock.sin_port = htons(6000); addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 发送到本机地址的任意地址 int num; num = WSAGetLastError(); // 绑定 int rebind; rebind = bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)); num = WSAGetLastError(); // 判断 是否绑定成功 if (SOCKET_ERROR == rebind) { MessageBox(_T("绑定失败")); closesocket(m_socket); return false; } return true; } DWORD WINAPI CChatDlg::RecvPro(LPVOID lpParameter) { // 定义变量来接收传递的参数 SOCKET sock = ((recvPARAM*)lpParameter)->recvSock; HWND hwnd = ((recvPARAM*)lpParameter)->recvHandle; // 接收数据 // 定义地址变量,来接收发送端地址信息 SOCKADDR_IN addrFrom; int len = sizeof(SOCKADDR); char recvBuff[200];// 接收数据变量 char tempBuffer[200]; //存放格式化之后的数据 int reVal; // while 循环 持续接收 while (1) { memset(recvBuff, 0, 200); int num; num = WSAGetLastError(); reVal = recvfrom(sock, recvBuff,200,0,(SOCKADDR*)&addrFrom,&len); // strlen(recvBuff) --> 200 if (SOCKET_ERROR == reVal) { num = WSAGetLastError(); AfxMessageBox(_T("接收数据失败!")); break; } else { char addrStr[100]; memset(tempBuffer,0,200); inet_ntop(AF_INET, &addrFrom.sin_addr.S_un.S_addr,addrStr,sizeof(SOCKADDR)); sprintf_s(tempBuffer, "%s 说:%s", addrStr, recvBuff); // 将数据传给对话框 // 采用 发送消息的方式 ::PostMessage(hwnd, WM_RECVDATA,0, (LPARAM)tempBuffer); } } return 0; } // 消息响应函数的实现 LRESULT CChatDlg::OnrecvDate(WPARAM wParam, LPARAM lParam) { //定义变量,取出参数中的数据 CString revStr = (char*)lParam; CString revStrOld; // 接收旧的数据 GetDlgItemText(IDC_EDIT_RECV, revStrOld); revStr += "\r\n"; revStrOld += revStr; // 新数据加上旧数据 SetDlgItemText(IDC_EDIT_RECV, revStrOld); return 0; } void CChatDlg::OnBnClickedBtnSend() { // TODO: 在此添加控件通知处理程序代码 // 获取 IP 地址对话框中的地址数据 DWORD dwIP; ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); SOCKADDR_IN addrTo; addrTo.sin_family = AF_INET; addrTo.sin_port = htons(6000); addrTo.sin_addr.S_un.S_addr = htonl(dwIP); // 定义变量获取发送框的数据 CString sendStr; GetDlgItemText(IDC_EDIT_SEND,sendStr); // 发送数据 sendto(m_socket, sendStr, sendStr.GetLength()*1,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR)); int num; num = WSAGetLastError(); // 将发送的文本框设为空 SetDlgItemText(IDC_EDIT_SEND,""); }

结果图:

最新回复(0)