MFC多人在线聊天室

2023-05-16


我已经在我的资源里上传了这个聊天室的代码了

基于MFC的C++的select模型的TCP聊天室
采用select网络模型,支持多人同时登陆,功能有上线、下线、群聊、私聊
使用CjsonObject进行数据传递,使用了心跳包进行判断下线的情况。

在这里插入图片描述
在这里插入图片描述

服务端并未使用MFC框架,解决方案包含文件如下:
在这里插入图片描述
主函数所在
ChatingServer.cpp

// ChatingServer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include"Server.h"
using namespace std;

int main()
{
	Server  tcpServer;
	tcpServer.CreateServer("127.0.0.1", 9527);
	tcpServer.RunServer();

}

创建一个Server类对象,调用其方法开启服务端。

Server类:
Serve.h

#pragma once
#include "common.h"
#include "CJsonObject.hpp"
#include "TcpSocket.h"
#include "CLock.h"
#include <time.h>
#include <string>
#include <list>
#include <utility>
#include <algorithm>
#include<iostream>

using namespace std;

class Server
{
public:
	Server();
	~Server();

	BOOL  CreateServer(const char* szIp, u_short nPort);
	BOOL  RunServer();
	CLock g_lock;
private:
	static DWORD WINAPI HandleClientsThread(LPVOID pParam);

private:
	class ClientInfo
	{
	public:
		ClientInfo(clock_t cc,list<ClientInfo*>*pLstClients): //初始化
			m_clockHeartTime(cc),m_pLstClients(pLstClients)
		{}
		clock_t m_clockHeartTime; //心跳包,上次心跳时间
		list<ClientInfo*>* m_pLstClients;
		CTcpSocket m_tcpsocketClient;
	};

private:
	CTcpSocket m_tcpSocket;
	list<ClientInfo*> m_lstClients;

public:
	bool HandleData(ClientInfo* pCL);

};


用户接口为创建服务的CreateServer和开启服务的RunServer

内部结构中,定义一个ClientInfo结构体存储客户端信息,包括该客户端上次发送心跳包的时间,但没有实例化,待会来看如何实例化。

先看一下m_tcpSocket,它是一个CTcpSocket类的对象,来看一下它的结构:

class CTcpSocket
{
public:
	CTcpSocket();
	~CTcpSocket();

	BOOL CreateSocket();
	BOOL BindListen(char* szIp, u_short nPort);
	BOOL Accept(CTcpSocket* pTcpSocket );
	BOOL Connect(char* szIp, u_short nPort);
	BOOL Recv(char* pBuff, int* pLen);//收数据
	BOOL Send(char* pBuff, int* pLen);//发数据


	BOOL RecvPackage(DATAPACKAGE* pPackage);
	BOOL SendPackage(DATAPACKAGE* pPackage);


	void CloseSocket();

	const sockaddr_in& GetSockaddrIn()const;
	SOCKET m_socket;

private:

	sockaddr_in m_si;
};

看来是将套接字的功能封装在一起了

定义一个用户处理线程HandleClientsThread,用于处理TCP连接请求。注意,MFC中的线程必须被声明成DWORD WINAPI

查看WINAPI的定义,它是这样定义的
#define WINAPI _stdcall
可以发现CALLBACK也是这样定义的
_stdcall规定了编译时的一些选项
WINAPI是一个宏,所代表的符号是__stdcall, 函数名前加上这个符号表示这个函数的调用约定是标准调用约定,windows API函数采用这种调用约定。
具体来说,他们是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是压入堆栈的内容由谁来清除,调用者还是函数自己?
stdcall的调用约定意味着:
1)参数从右向左压入堆栈;
2)函数自身修改堆栈;
3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸。

好了,Server的结构我们了解了,看一下调用Server成员函数的情况
CreateServer的实现

BOOL Server::CreateServer(const char* szIp, u_short nPort)
{
	//1.创建tcp客户端
	BOOL bRet = m_tcpSocket.CreateSocket();
	if (!bRet)
	{
		cout << "tcp客户端创建失败" << endl;
		return FALSE;
	}

	//2.绑定端口
	bRet = m_tcpSocket.BindListen((char*)"127.0.0.1", 9527);
	if (!bRet)
	{
		cout << "绑定端口监听失败" << endl;
		return FALSE;
	}

	//3.创建一个线程,用来接收客户端数据
	HANDLE hThread = CreateThread(NULL, 0, HandleClientsThread, (LPVOID)this, 0, NULL);
	CloseHandle(hThread);

	return TRUE;
}

分开来看,其中CreateSocket封装了socket的初始化

BOOL CTcpSocket::CreateSocket()
{
	m_socket = socket(
		AF_INET,
		SOCK_STREAM, //流式 
		IPPROTO_TCP);//tcp协议
	if (m_socket == SOCKET_ERROR)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}

接着调用BindListen

BOOL CTcpSocket::BindListen(char* szIp, u_short nPort)
{
	// 	2) 绑定端口
	m_si.sin_family = AF_INET;
	m_si.sin_port = htons(nPort);
	m_si.sin_addr.S_un.S_addr = inet_addr(szIp);
	int nRet = bind(m_socket, (sockaddr*)&m_si, sizeof(m_si));
	if (nRet == SOCKET_ERROR)
	{
		return FALSE;
	}

	// 	3) 监听
	nRet = listen(m_socket, SOMAXCONN);
	if (nRet == SOCKET_ERROR)
	{
		return FALSE;
	}

	return TRUE;
}

可以看出,是绑定端口并监听的封装。
最后建立处理线程,用来接收客户端数据。

接下来调用RunServer,接受来自客户端的数据

BOOL Server::RunServer()
{
	//接受来自客户端的数据
	while (TRUE)
	{
		ClientInfo* pCI = new ClientInfo(clock(), &m_lstClients);
		BOOL bRet = m_tcpSocket.Accept(&pCI->m_tcpsocketClient);
		if (!bRet)
		{
			break;
		}
		printf("IP:%s port:%d 连接到服务器. \r\n",
			inet_ntoa(pCI->m_tcpsocketClient.GetSockaddrIn().sin_addr),
			ntohs(pCI->m_tcpsocketClient.GetSockaddrIn().sin_port));

		m_lstClients.push_back(pCI);

	}
	return 0;
}

其中的Accept是对accept的封装,用于接受连接

BOOL CTcpSocket::Accept(CTcpSocket* pTcpSocket)
{
	// 	4) 接受连接
	sockaddr_in siClient;
	int nSize = sizeof(siClient);
	SOCKET sockClient = accept(m_socket, (sockaddr*)&siClient, &nSize);
	if (sockClient == SOCKET_ERROR)
	{
		return FALSE;
	}

	pTcpSocket->m_socket = sockClient;
	pTcpSocket->m_si = siClient;
	return TRUE;
}

回到RunServer中继续分析:

ClientInfo* pCI = new ClientInfo(clock(), &m_lstClients);

他会调用ClientInfo的构造函数:

	class ClientInfo
	{
	public:
		ClientInfo(clock_t cc,list<ClientInfo*>*pLstClients):  //初始化
			m_clockHeartTime(cc),m_pLstClients(pLstClients)
		{}
		clock_t m_clockHeartTime; //心跳包,上次心跳时间
		list<ClientInfo*>* m_pLstClients;
		CTcpSocket m_tcpsocketClient;
	};

&m_lstClients是传引用,即之前在main中创建Server类对象tcpServer时实例化的那个,他是一个双向链表,既然是传引用,容易看出,所有的客户端都共享同一个双向链表。

一旦接受连接,存储客户端信息,由于传递的是引用,pTcpSocket亦即RunServer中的pCI,并在RunServer中判断接受成功后,将客户端信息打印到控制台上。

printf("IP:%s port:%d 连接到服务器. \r\n",
			inet_ntoa(pCI->m_tcpsocketClient.GetSockaddrIn().sin_addr),
			ntohs(pCI->m_tcpsocketClient.GetSockaddrIn().sin_port));

若没有连接,则会跳出本次循环,继续调用Accept查询链接。

		if (!bRet)
		{
			break;
		}

连接成功后,还会向m_lstClients这个list中添加客户端信息

m_lstClients.push_back(pCI);

另一边HandleClientsThread这个线程不断从m_lstClients这个list中遍历存储连接信息,调用select来选择处理哪个连接,再调用HandleData()处理数据,包括判断数据类型,群发私聊等。

自己画的思维导图,有点乱。。。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC多人在线聊天室 的相关文章

  • kuangbin连通图专题,Network of Schools (POJ - 1236,tarjan算法求缩点+缩点的入度与出度的运用)

    kuangbin 题意是有N个学校 每个学校可能向几个学校进行数据传输 xff08 单向的 xff09 问 至少需要把一个文件给几个学校可以使给的N个学校都收到文件 再问在加几个通信线路可以使各个学校之间都能直接或间接的传递文件 一个强连通
  • endnote 文献保留前三个作者

    1 问题描述 xff1a endnote使用GBT7714文献格式 xff0c 显示文献的全部作者 2 想要达到的效果 xff1a 最多显示三个作者 3 解决方法 xff1a 还不知道怎么弄 xff0c 看看以后再来补充 xff0c 心情烦
  • RTK_LIB 源码、可执行文件、rtkget、观测文件、星历文件(精密星历、广播星历)、精密钟差文件介绍

    1 RTK LIB开源程序下载 xff1a 点开rtklib链接 xff1a 下载最新版本的可执行文件和程序源码 2 GNSS数据处理需要的文件 2 1 伪距定位 xff1a spp 观测数据 xff08 0 xff09 导航星历 广播星历
  • RTKLIB ppp rtk_post

    1 实时ppp xff1a IGS MGEX数据处理中心的播发的实时轨道钟差产品 xff0c 结合广播星历 xff0c 实现实时定位 2 事后的 xff08 近似实时 xff09 xff1a 下载精密星历 钟差产品 xff0c 结合其他的精
  • 4.使用静态库、动态库,常见问题解决

    使用动态库的流程 xff08 ORBSLAM3实例 xff09 xff1a 在调用动态库或静态库的 cpp文件添加库的头文件 xff08 可以包含路径 xff0c 也可以在cmakelist文件加头文件搜索路径 xff09 span cla
  • vscode查看代码更新历史

    开源代码推出新版本后 xff0c 如何查看代码更改信息 1 首先打开vscode xff0c 点击左侧的插件管理器 xff0c 进入插件面板 xff0c 搜索Git Graph并安装 2 点击下图图标 xff0c 即可进入Git Graph
  • git更新代码

    一 git一般有很多分支 xff0c 我们clone到本地的时候一般都是master分支 xff0c 那么如何切换到其他分支呢 xff1f 主要命令如下 xff1a 1 查看本地分支文件信息 xff0c 确保更新时不产生冲突 span cl
  • linux---硬链接和软链接

    文件系统 磁盘上文件读写存储与查找系统 xff08 管理 xff09 就是文件系统 xff0c 在每一个分区都会存在自己的文件系统 在这里我们有swap交换分区和文件分区 xff0c 我们这里只介绍文件分区 在文件分区都会有上图中的分块管理
  • char类型数组

    字符数组 xff08 一维 二维 xff09 字符数组是数组元素为char类型的一种数组 凡是适合数组的定义和赋值 xff0c 也都适合于字符数组 由于C语言没有提供字符串类型 xff0c 字符串一般用一维字符数组来存放 xff0c 而二维
  • ubuntu18.04 安装腾讯会议

    腾讯会议现在以及上线了Linux版本 xff0c 可以直接在腾讯会议官网下载linux 版本 xff0c 在官网点击免费下载 xff0c 可以直接下载Linux版本 腾讯会议下载链接 选择Linux版本 xff0c x86 64版本 xff
  • 2.树莓派系统备份

    树莓派使用SD卡来装载系统 xff0c 如果SD卡丢失或者损坏 xff0c 那么树莓派上的数据都会丢失 xff0c 所以一定要备份好SD卡 这篇文章可以帮你备份你的树莓派系统 主要内容为备份SD卡 xff0c 制作树莓派系统镜像以及在需要的
  • ic_gvins编译及环境配置问题解决

    RTK VIO松组合 对惯导精度要求较高 1 环境配置和编译 安装依赖项 span class token comment gcc 8 span span class token function sudo span span class
  • EVO画图设置

    一 绘图设置 1 更改背景色和网格 span class token comment 白色网格 span evo config span class token builtin class name set span plot seabor
  • GINS_OB环境配置

    1 程序简介 武大开源GNSS INS松组合IMU预积分有考虑地球自传和不考虑两种形式可以灵活设置GNSS中断时间IMU可以和里程计进行融合 2 环境配置 span class token comment gcc 8 g 43 43 8 s
  • OB_GINS程序框架

    1 程序运行 span class token builtin class name cd span OB GINS span class token comment 编译好的可执行文件 xff1a bin ob gins xff0c 参数
  • KEIL、MDK中关于__LINE__宏 printf 的显示不正确的问题

    span class token operator gt span define span class token function DEBUG span span class token punctuation span log span
  • VINS-回环检测与重定位

    参考博客 pose graph分析1 pose graph分析2 pose graph分析3
  • 源码安装naviagtion,但是出现[move_base-2] process has died 运行错误的解决办法

    今天开始记录ros遇到的问题 安装navigation可以使用两种方法 第一种 xff1a sudo apt get install ros kinetic navigation 这种安装方法最简单 xff0c 新手或者不需要动naviag
  • linux---静态库和动态库的制作和使用

    静态链接和动态链接 静态链接 xff1a 生成可执行代码 xff0c 链接静态库 xff08 与代码位置有关的链接方式 xff09 xff0c 需要将代码拷贝到我们的源代码中才能运行 动态链接 xff1a 生成可执行代码 xff0c 链接动
  • 加一

    加一 描述 给定一个由整数组成的非空数组所表示的非负整数 xff0c 在该数的基础上加一 最高位数字存放在数组的首位 xff0c 数组中每个元素只存储单个数字 你可以假设除了整数 0 之外 xff0c 这个整数不会以零开头 示例 1 输入

随机推荐

  • STM32bootloader原理解释

    STM32bootloader原理解释 一 STM32的常规启动流程 STM32的内部flash地址起始于0x8000000 xff0c 一般情况下 xff0c 程序文件就从此地址开始写入 此外STM32是基于Cortex M3内核的微控制
  • 模糊PID基本原理及matlab仿真实现(新手!新手!新手!)

    有关模糊pid的相关知识就把自己从刚接触到仿真出结果看到的大部分资料总结一下 xff0c 以及一些自己的ps 以下未说明的都为转载内容 1 转自 https blog csdn net weixin 36340979 article det
  • VMware+ubuntu+win10笔记本实现笔记本连接WIFI且ubuntu既可以上网又能连接开发板

    背景 最近在学习imx6ull开发板的时候 xff0c 发现开发板通过网线连接笔记本电脑却无法ping通ubuntu xff0c 于是捣鼓了很久终于可以了 xff0c 却又发现ubuntu不能上网了 xff0c 经过一番查找资料和尝试 xf
  • 在windows上用vscode打造比vc++6.0好用的C/C++ IDE,适用编程小白

    准备 xff1a 1 安装MinGW xff0c 添加gcc gdb等编译调试工具bin目录 头文件Include目录 库lib的路径到系统环境变量 xff0c 安装LLVM 添 加Clang编译器所在bin目录到系统环境变量 具体操作百度
  • C语言数据结构——线性表的链式存储结构

    文章目录 线性表的链式存储结构1 基本概念2 设计与实现3 优点和缺点 线性表的链式存储结构 1 基本概念 链式存储定义 xff1a 为了表示每个数据元素与其直接后继元素之间的逻辑关系 xff0c 每个元素除了存储本身的信息之外 xff0c
  • 智能车浅谈——硬件篇

    目录 初识小车硬件系统1 电源系统线性电源开关电源 2 人机交互系统3 MCU最小系统4 传感器系统摄像头电感编码器 5 驱动系统 机械结构 17届完赛代码智能车系列文章汇总 前言 xff1a 作为一名老三本玩家 xff0c 笔者深知一些同
  • 智能车浅谈——图像篇

    文章目录 前言认识图像基本含义图像类型数字图像彩色图像灰度图像黑白图像 小结 图像处理图像压缩二值化固定阈值法大津法 图像降噪 xff08 腐蚀 xff09 寻边线 总结17届完赛代码17届完赛代码智能车系列文章汇总 前言 前面已经记录了智
  • 智能车浅谈——手把手让车跑起来(电磁篇)

    文章目录 前言材料准备备赛组车模硬件 练习组车模硬件方案 整车原理赛道信息获取及转向原理工字电感运放模块转向原理元素判断 电机及舵机控制原理 代码实现效果欣赏总结17届完赛代码智能车系列文章汇总 前言 电磁寻迹小车 之前智能车系列已经做了一
  • 手把手教你OneNET数据可视化

    文章目录 前言OneNET实现数据可视化效果一览发布项目 xff08 5 17更新 xff09 总结 前言 之前介绍了Hi3861使用MQTT协议接入OneNET实现数据的上传以及命令的下发 xff0c 本文主要是介绍一下如何使用OneNE
  • linux---进程间通信(ipc)之管道

    进程间通信方式 管道共享内存消息队列信号量本地套接字等等都能作为我们进程间通信的方法 操作系统提供进程间通信方式的原因 因为对于我们进程来说 xff0c 每一个进程都是相互独立的 xff0c 具有独立性 xff0c 如果我们需要两个不同的进
  • 嵌入式学习笔记——STM32的USART收发字符串及串口中断

    USART收发字符串及串口中断 前言字符串的收发发送一个字符串接收字符串需求 利用串口实现printf 中断中断是什么串口的接收中断以及空闲中断实现代码实现效果 总结M4系列目录 前言 上一篇中 xff0c 介绍了串口收发相关的寄存器 xf
  • 嵌入式学习笔记——PWM与输入捕获(下)

    输入捕获 前言输入捕获的概述框图输入通道部分比较捕获寄存器与事件生成 寄存器编程思路 实际需求配置流程打开对应的时钟配置GPIO为复用模式定时器的时基部分配置定时器输入通道部分配置定时器中断配置 代码 xff1a 运行效果 xff1a 需求
  • 嵌入式学习笔记——SPI通信的应用

    SPI通信的应用 前言屏幕分类1 3OLED概述驱动芯片框图原理图通信时序显示的方式页地址 列地址初始化指令 程序设计初始化代码初始化写数据与写命令清屏函数 初始化代码字符显示函数 总结M4系列目录 前言 上一篇中介绍了STM32的SPI通
  • 嵌入式学习笔记——IIC通信

    IIC通信 前言IIC概述通信特征物理拓扑结构IIC通信的流程IIC的特点 xff1a STM32的IIC通信GPIO模拟IICIIC的时序组成 xff08 主机对从机写入数据 xff09 1 起始信号2 器件地址与读写位3 从机应答信号5
  • 立创梁山派学习笔记——GPIO输出控制

    梁山派 前言开发板简介GD32F407ZGT6官方资源数据手册1 系统框图2 引脚复用表3 命名规则4 其他 用户手册固件库与PACK包 开发环境搭建立创官方的资料包资料齐活 xff0c 开发1 工程搭建2 使用寄存器点亮LEDGPIO数量
  • C51_day5:串口通信UART

    3 1 串口基本认知 串行接口简称串口 xff0c 也称串行通信接口或串行通讯接口 xff08 通常指COM接口 xff09 xff0c 是采用串行通信方式的扩展接口 串行接口 xff08 Serial Interface xff09 是指
  • 哈希表/哈希冲突及解决方法(较全)

    哈希表的概念请参阅他人文章 xff0c 关于哈希冲突的解决这篇文章基本都整理到了 xff0c 还有几个常见的面试题 解决hash冲突的几种方法 前导 xff08 题外话 xff09 xff1a 一 开放定址法 xff08 闭散列 xff09
  • 关于构造函数,拷贝构造函数,析构函数的调用顺序(1)

    导言 对象是由 底层向上 开始构造的 xff0c 当建立一个对象时 xff0c 首先调用基类的构造函数 xff0c 然后调用下一个派生类的构造函数 xff0c 依次类推 xff0c 直至到达派生类次数最多的派生次数最多的类的构造函数为止 因
  • vector的内存释放

    xff11 vector内存分配机制 C 43 43 中vector的一个特点是 xff1a 内存空间只会增长 xff0c 不会减小 即为了支持快速的随机访问 xff0c vector容器的元素以连续方式存放 xff0c 每一个元素都挨着前
  • MFC多人在线聊天室

    我已经在我的资源里上传了这个聊天室的代码了 基于MFC的C 43 43 的select模型的TCP聊天室 采用select网络模型 xff0c 支持多人同时登陆 xff0c 功能有上线 下线 群聊 私聊 使用CjsonObject进行数据传