Introduction 若要判断一个输入的 QQ 号是否有效,你会如何处理呢? 首先你得分析一下其对应规则,依次列出: 长度大于5,小于等于11; 首位不能为0; 是否为纯数字? 规则既列,接着就该尝试实现了,那么用什么来表示字符串呢?在 C++ 中,最容易想到的就是 string,其中提供了许多成员函数可以处理字符串,所以有了如下实现: std::string qq; std::cin >> qq; // 1. 判断位数是否合法 if (qq.length() >= 5 && qq.length() <= 11) { // 2. 判断是否非'0'开头 if (qq[0] != '0') { // 3. 判断是否为纯数字 auto pos = std::find_if(qq.begin(), qq.end(), [](const char& ch) { return… Continue Reading C++ 正则表达式

前言 这是IOCP的末篇了,本次的实例使用IOCP配合扩展函数来实现服务器,并对之前的版本做一些优化,比如这里使用了内存池,日志记录,所以这也是效率最好的一个版本,作为一个例子来说已经很完整了。 因为前面已经介绍了较多的基础内容,并且也写出了实例一,所以这篇大部分内容都不会再详细讲解相关内容了,这写起来太费时间了。取而代之的是代码会全部列出来,若大家有哪里看不懂,那就说明前三篇未全部理解,这时应该再看看前面的文章。 类预览 首先来预览下类的定义,看看我们需要做些什么: #include <thread> // 线程库 #include <mutex> // 线程同步互斥量 #include <string> // 不用说 #include <memory> // 用到智能指针 #include <cassert> // 断言 #include <vector> // 使用vector保存连接的用户 #include <algorithm> // 算法库 #include <boost/pool/singleton_pool.hpp> // 内存池 #include <boost/format.hpp> // 格式化字符 #include <WinSock2.h> // winsock2 #include <MSWSock.h> // 扩展函数 #include… Continue Reading 网络模型之IOCP服务器实例二(四)

上篇用IOCP实现了一个简单的服务器,在处理消息方面性能已经不错了,但是接爱请求函数却依旧使用的是 accept 函数,所以这部分性能并不够,而Windows在扩展函数中为我们提供了一些选择,本篇就来介绍这些函数。 AcceptEx 这个函数用来异步地投递一个调用来接受客户端连接,其原型如下: BOOL AcceptEx ( SOCKET sListenSocket, // 监听套接字 SOCKET sAcceptSocket, // 分配给待连接客户端的套接字 PVOID lpOutputBuffer, // 用来接收用户第一份数据的缓冲区 DWORD dwReceiveDataLength, // 缓冲区的字节数 DWORD dwLocalAddressLength, // 本地套接字地址结构大小 DWORD dwRemoteAddressLength, // 远程套接字地址结构大小 LPDWORD lpdwBytesReceived, // 新建的客户机连接上所收到的字节数 LPOVERLAPPED lpOverlapped // 重叠结构 ); 这个函数稍微比 accept 函数麻烦了一点,那么在这里我们列出 accept 的原型来对比着展开介绍: SOCKET accept ( SOCKET… Continue Reading 网络模型之IOCP与扩展函数(三)

上篇简单地介绍了IOCP模型所需的基础内容,并给出了服务器版本一的声明,更多的内容会在本篇的实现中来展开说明,学完这篇就基本会明白怎样用IOCP来实现一个还不错的上万级别的服务器了。 开始之前得对上篇的类声明做一些改变,因为前天本来是用智能指针来管理客户端单句柄数据和单IO数据的,但当我那晚实现完后测试发现有点问题。当客户端程序低并发访问时没有任何问题,但在用脚本同时开很多客户端时有些客户端得不到正常关闭,这就说明服务器有问题了。开始我以为是线程同步的问题,跟踪了好久发现原来是智能指针的问题。在收到用户连接请求准备接收数据后就会准备接收下一位用户连接了,而在处理IO消息的线程中并非一直拥有指针的所有权,会在退出当前线程时丢失那么一小会儿,一当智能指针的引用计数变为0时原来分配的单IO数据就被释放了,但稍后却还需要用到它,因为已被释放所以稍后会导致访问已被释放的内存,就算能访问到里面也是一堆垃圾数据,后面的操作就得不到执行了,客户端也就无法正常关闭了。 所以在这种多线程中智能指针的使用也得格外小心,于是我换成了普通指针,之后没有任何问题。来看看改变后的类声明: class IOCPServer { public: IOCPServer(int port); ~IOCPServer(); void Accept(); private: void InitSock(); // 初始化套接字 void CreateIocp(); // 创建IOCP并开启线程 void AcceptHandler(); // 接受处理 void RequestHandler(); // IO消息处理线程 void RecvMsg(SOCKET, LPPER_IO_DATA); // 接收消息 void SendMsg(SOCKET, LPPER_IO_DATA, const std::string&); // 发送消息 void CloseSock(SOCKET, LPPER_HANDLE_DATA, LPPER_IO_DATA); // 关闭套接字 private: std::shared_ptr<PER_HANDLE_DATA> m_servSock;… Continue Reading 网络模型之IOCP实现版本一(二)

前言 前面介绍了重叠IO模型,该模型的缺点是发出IO请求的线程必须同时对完成通知进行处理,若一个线程发出多个请求,那么即使其它线程完全处于空闲状态,该线程也必须对每个请求的完成通知做出响应,从而影响了性能。 为了解决这个问题,我们可以在主线程中调用 accept 函数,再单独创建 1 个线程来负责客户端IO。实际上,IOCP模型便是创建了专用的线程来处理客户端IO,不过为了充分发挥 CPU 性能,它创建了不止一个线程。 所以IOCP模型其实就是对于重叠IO模型的更加完善,在阅读本篇之前,需要懂得如何使用重叠IO模型。 在学习各种网络模型之时,我们主要应该去关注两点,在select模型的时候大家就看过这个图: 实际上这两点也就是理解各个模型的关键,我们要关注如何等待客户端连接,还有如何将数据从网卡缓冲区拷贝到程序缓冲区。这两部分若有一部分阻塞,那么性能就不是太好。 IOCP(I/O Completion Port),即IO完成端口模型,该模型会创建一个CP(完成端口)内核对象,然后将CP对象与套接字绑定起来,之后套接字若有IO消息便可通过相关函数获得到。 创建CP对象 使用如下函数创建一个CP对象: HANDLE CreateIoCompletionPort ( HANDLE FileHandle, // file handle to associate with // the I/O completion port HANDLE ExistingCompletionPort, // handle to the I/O completion port DWORD CompletionKey, // per-file completion key for I/O… Continue Reading 网络模型之IOCP基础(一)