C++网络编程(二)--客户端服务器程序
这是一个基于windows的,用C++编写的客户端服务器程序,适合初学者,高手误入.源码必共享
思路是这样的.启动服务器,服务器启动后会创建一个子线程,用于向客户端发送信息.用一个死循环用于接收客户端的请求,客户端请求成功后,会将客户端的连接保存到一个集合中,下面会详细介绍这个保存客户端连接的类.客户端连接成功后,服务器会创建一个子线程用于接收客户端的信息,客户端同样也会创建一个子线程接收服务器的信息.这样客户端和服务器就能进行通讯,如果有哪一方退出,另一方对应的接收数据的线程就会自动终止.
退出一个客户端后,服务器对应的接收数据的线程自动终止.如下图:
服务器保存客户端连接的集合中会删除对应的客户端连接,由于这个删除操作是在子线程中发生的,也就是说会有多个线程操作这个集合,那么针对这个集合的操作必须是线程安全的.保证线程安全的方法又很多,我的这篇博客《多线程编程--5种方法实现线程同步》介绍了5中方法实现线程同步,我这里用的是关键段,还有一点值得说明的是,保存客户端连接的集合肯定只能有一份,我用一个类封装了这个集合,这个类中的每个方法都是线程安全的,且只能有一个实例,这里用了比较暴力的方法,将相关的方法设为private,提供一个public的方法返回这个对象的一个静态实例,唯一的一个实例。
保存客户端连接的类如下:
//ClientList.h 存放客户端的请求,只能有一个实例 #ifndef _CLIENTLIST_H_ #define _CLIENTLIST_H_ #include <vector> #include "CSocket.h" #include <assert.h> class CSocket; class ClientList { public : typedef vector<CSocket*>::iterator Iter; void Add(CSocket* socket); int Count() const; CSocket* operator[](size_t index); void Remove(CSocket* socket); Iter Find(CSocket* socket); void Clear(); static ClientList* GetInstance() { static ClientList instance; return &instance; } ~ClientList(); private: static CRITICAL_SECTION g_cs; static vector<CSocket*> _list; ClientList(); ClientList(const ClientList&); ClientList& operator=(const ClientList&); }; #endif
#include "ClientList.h" typedef vector<CSocket*>::iterator Iter; ClientList::ClientList() { InitializeCriticalSection(&g_cs);//初始化g_cs的成员 } ClientList::~ClientList() { DeleteCriticalSection(&g_cs);//删除关键段 } void ClientList::Add(CSocket* socket) { if(socket!=NULL) { EnterCriticalSection(&g_cs);//进入关键段 _list.push_back(socket); LeaveCriticalSection(&g_cs);//退出关键段 } } int ClientList::Count() const { return _list.size(); } CSocket* ClientList::operator[](size_t index) { assert(index>=0 && index<_list.size()); return _list[index]; } void ClientList::Remove(CSocket* socket) { Iter iter=Find(socket); EnterCriticalSection(&g_cs);//进入关键段 if(iter!=_list.end()) { delete *iter; _list.erase(iter); } LeaveCriticalSection(&g_cs);//退出关键段 } Iter ClientList::Find(CSocket* socket) { EnterCriticalSection(&g_cs);//进入关键段 Iter iter=_list.begin(); while(iter!=_list.end()) { if(*iter==socket) { return iter; } iter++; } LeaveCriticalSection(&g_cs);//退出关键段 return iter; } void ClientList::Clear() { EnterCriticalSection(&g_cs);//进入关键段 for(int i=_list.size()-1;i>=0;i--) { delete _list[i]; } _list.clear(); LeaveCriticalSection(&g_cs);//退出关键段 } CRITICAL_SECTION ClientList::g_cs; vector<CSocket*> ClientList::_list ;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库