当前位置: 首页 > news >正文

C++打造局域网聊天室第九课: 客户端队列及其处理线程

文章目录

  • 前言
  • 一、添加客户端队列的参数初始化
  • 二、相关函数
  • 总结


前言

C++打造局域网聊天室第九课: 客户端队列及其处理线程


一、添加客户端队列的参数初始化

在Server.cpp的 ListenThreadFunc()函数内的其他操作处实现客户端队列的添加。
在这里插入图片描述
首先进行部分参数的初始化

CClientitem tItem; //定义一个客户端结点
tItem.m_Socket = accSock; //客户端的socket即为accept函数返回的accSock
tItem.m_surlp = inet_ntoa(clientAddr.sin_addr); // 客户端的IP包含在clientAddr结构中。inet_ntoa()函数将网络字节顺序的Ip转化为本机格式的字符串(192.192.1.1)
tItem.m_pMainWnd = pChartRoom; //主线程对话框指针

此时编译会报错: ‘inet_ntoa’: Use inet_ntop() or InetNtop() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings chartroom d:\vs2017项目\打造局域网聊天室\chartroom\chartroom\server.cpp 100

这是由于函数inet_ntoa过旧,此时需要在项目的属性中把SDL检查设置为否。
在这里插入图片描述

二、相关函数

后续的客户端队列的建立过程需要依赖一些函数,我们先实现这些函数,再回来继续客户端队列的建立过程。

聊天信息显示函数:
由于服务端每次都需要显示全部的聊天记录,而不是只显示当前的聊天记录,这就需要我们对之前的聊天内容进行保存。
首先对显示聊天内容的编辑框绑定一个成员变量。
在这里插入图片描述
设置一个变量名称,其他都为默认值
在这里插入图片描述
此时在chartroomDlg.h头文件内会自动声明该变量。
在这里插入图片描述
并且在chartroomDlg.h头文件中声明一个函数,用于声明聊天信息显示函数
在这里插入图片描述
在chartroomDlg.cpp源文件中实现函数ShowMsg()

// 实现聊天信息显示函数
void CchartroomDlg::ShowMsg(CString strMsg)
{m_MsgEdit.SetSel(-1, -1); // 将光标定位到文字的最后一个位置m_MsgEdit.ReplaceSel(strMsg + _T("\r\n")); // 将光标替换为strMsg + _T("\r\n")
}

从队列中删除客户端函数:
如果客户端关闭,此时服务端对应的客户端结点还在客户端队列中,那么就需要将客户端结点从客户端队列中删除。

同样先在chartroomDlg.h头文件内声明函数,使得类CchartroomDlg中包含成员函数
在这里插入图片描述
在chartroomDlg.cpp源文件中实现函数RemoveClientFromArray()

// 实现从队列删除客户端结点函数
void CchartroomDlg::RemoveClientFromArray(CClientitem in_Item) //参数为需要删除的客户端结点
{for (int idx = 0; idx < m_ClientArray.GetCount(); idx++) // 遍历客户端队列中的每一个结点{CClientitem tItem = m_ClientArray.GetAt(idx);if (tItem.m_Socket == in_Item.m_Socket && tItem.hThread == in_Item.hThread && tItem.m_surlp == in_Item.m_surlp) //队列中的该结点为需要删除的结点{m_ClientArray.RemoveAt(idx); // 删除结点}}
}

现在我们回到Server.cpp文件中,继续客户端队列的建立过程。
接下来初始化该客户端结点的线程句柄,同服务端创建线程,利用函数 CreateThread()。

// 客户端队列的添加CClientitem tItem; //定义一个客户端结点tItem.m_Socket = accSock; //客户端的socket即为accept函数返回的accSocktItem.m_surlp = inet_ntoa(clientAddr.sin_addr); // 客户端的IP包含在clientAddr结构中。inet_ntoa()函数将网络字节顺序的Ip转化为本机格式的字符串(192.192.1.1)tItem.m_pMainWnd = pChartRoom; //主线程对话框指针//  每一个客户端对应线程的句柄。说明:一个服务端对应多个客户端,在while死循环中调用accept接受一个客户端的连接之后,还可以在其他循环时接受// 其他客户端连接,每有一个客户端连接,我们创建一个客户端结点,将客户端结点的信息加入到客户端队列当中。//!!!!!!!!注意:该线程只进行监听端口,等待连接客户端的操作,接受连接客户端传来的消息等功能还需要新开一个线程进行处理,新开的线程与连接的客户端进行通信INT_PTR idx = pChartRoom->m_ClientArray.Add(tItem); // 将客户端结点放入队列中tItem.hThread = CreateThread(NULL, 0, ClientThreadProc, &(pChartRoom->m_ClientArray.GetAt(idx)), CREATE_SUSPENDED, NULL);// 初始化该客户端结点的线程句柄,注意第四个参数不要传递tItem的地址,CREATE_SUSPENDED表示该线程不要立即执行//这是因为如果有多个客户端来连接,tItem会一直变为最新连接的客户端结点,但是之前调用的以前连接的客户端结点也是用tItem,此时tItem已经存储的没有//之前连接的客户端结点的内容了pChartRoom->m_ClientArray.GetAt(idx).hThread = tItem.hThread; //将tItem的线程句柄同时赋给客户端队列中对应的客户端的线程句柄// 至此,该客户端的结点信息填充完毕ResumeThread(tItem.hThread); //与CREATE_SUSPENDED对应,在这里才开始执行被CREATE_SUSPENDED修饰的线程

下面介绍该线程的程序ClientThreadProc()函数的具体实现

// 实现客户端的创建线程函数,与接受的客户端进行通信的线程函数
DWORD WINAPI ClientThreadProc(LPVOID IpParameter) // 传入的参数为&(pChartRoom->m_ClientArray.GetAt(idx))
{CString strMsg;CClientitem m_ClientItem = *(CClientitem*)IpParameter; // 将传入的地址利用强制类型转换为客户端结点类型,不使用地址是怕误修改while (TRUE) // 用于监听该连接的客户端是否发送消息{if (SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE)) //向缓冲区“偷看”一眼是否有客户端发过来的消息{TCHAR szBuf[MAX_BUF_SIZE] = { 0 }; // 申请缓冲区// recv函数:第一个参数为接收哪一个socket的数据;第二个参数为窄字节的缓冲区,存放接收过来的数据,要申请一块内存//第三个参数:缓冲区的长度;第四个参数平时为0即可。正常返回接收字节个数int iRet = recv(m_ClientItem.m_Socket, (char*)szBuf, MAX_BUF_SIZE,0); // 调用recv函数接受缓冲区中客户端发过来的消息if (iRet > 0){//正确,接收数据成功strMsg = szBuf; //为了操作方便(使用模板类函数),这里我们使用CString类型strMsg = _T("客户端:") + m_ClientItem.m_surlp + _T(">") + strMsg;m_ClientItem.m_pMainWnd->ShowMsg(strMsg); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示}else // 接收数据失败,有错误或者客户端关闭了{// 关闭socketstrMsg = _T("客户端") + m_ClientItem.m_surlp + _T("离开了聊天室!");m_ClientItem.m_pMainWnd->ShowMsg(strMsg); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem); // 利用在chartroom.cpp中实现的RemoveClientFromArray方法在客户端队列中删去该客户端结点break;// 跳出循环,客户端已下线,退出线程}}Sleep(500);}return TRUE;
}

总结

C++打造局域网聊天室第九课: 客户端队列及其处理线程

http://www.lryc.cn/news/505189.html

相关文章:

  • 请求go web后端接口 java安卓端播放视频
  • XML Schema 复合类型 - 混合内容
  • 第8章 搬移特性
  • ARM/Linux嵌入式面经(五九):海尔
  • java中的List、数组和set
  • freeswitch(配置文件结构)
  • ARMS 用户体验监控正式发布原生鸿蒙应用 SDK
  • 使用 esrally race 测试 Elasticsearch 性能:实践指南
  • OkHttp源码分析:分发器任务调配,拦截器责任链设计,连接池socket复用
  • 中国计算机学会计算机视觉专委会携手合合信息举办企业交流活动,为AI安全治理打开“新思路”
  • 重生之我在异世界学编程之C语言:深入预处理篇(上)
  • dolphinscheduler服务RPC框架源码解析(二)RPC核心注解@RpcService和@RpcMethod设计实现
  • 【从零开始入门unity游戏开发之——C#篇04】栈(Stack)和堆(Heap),值类型和引用类型,以及特殊的引用类型string
  • ARCGIS国土超级工具集1.2更新说明
  • 暂停window11自动更新
  • Git简介和特点
  • 如何通过docker 部署minio,端口号为9105
  • 设置Qt程序开机自启动(windows版本)
  • 【HarmonyOS】鸿蒙获取appIdentifier,Identifier
  • 【Rust自学】3.5. 控制流:if else
  • 美国信息学奥林匹克竞赛USACO 2024年12月比赛铜级问题1. 循环舍入-答案代码
  • Llama3模型详解 - Meta最新开源大模型全面解析
  • 2021-02-12 c++里面cin.sync()函数的意思
  • 下载红米Note 9 Pro5G对应的LineageOS代码下载及编译
  • 《探索 Caffe2 的 C++接口在移动设备上的性能优化之路》
  • 1.编写一个程序,给定一个大写字母,要求用小写输出
  • 条件随机场(CRF)详解:原理、算法与实现(深入浅出)
  • Android Studio、JDK、AGP、Gradle、kotlin-gradle-plugin 兼容性问题
  • 防抖(Debounce)和节流(Throttle)的区别和应用场景
  • 前端 Code Review 常见问题