第八十六讲 综合运用(1)

今天有点空,给大家说C++也有一段时间了,从开始到现在已经大半年了,说了不少C++的设计技巧,STL也说得差不多,当然还有string,io库这些也是挺有用的,不过就目前我们所掌握的string和io足够我们来应付一些简单的开发,说到开发,大家可能会有疑惑,有时候感觉C++都学得差不多了,为什么就写不出一个有用点的程序来呢?离开了书本就不知道该去干嘛了,所以,我就抽点时间出来,和大家一起来写一个简单的聊天程序。
这个想法来源于公司里面的一套监控软件,这套软件是用来监控公司设备状况和公司员工聊天用的,这里我展现出来的可能和公司里面的不一样,但是基本功能都差不多,如果大家只是跟着我的课程走到现在的,那么接下来的内容可能超出一部分的理解范围,但是没关系,有问题,可以提出来,当然有时间我会把里面涉及到的东西都和大家细说分享。
ok,我们先来聊聊主要功能,因为这里面我们将抛开公司的一些功能,我们就从聊天功能着手,说到聊天,相信有经验的朋友马上想起了socket,不错,我们这里就是用socket,因为是在windows上,所以我们这里使用winsocket,所以我们需要包含winSock2.h头文件,同时,我们还需要用到一些相关的扩展功能,所以还需要mswsock.h头文件,因为我们使用的是平台相关的,所以还需要windows.h头文件,好吧,我们现在来理一下需要的大概头文件。
===================================
#include <WinSock2.h>
#pragma comment(lib,"

ws2_32"

)
#include <mswsock.h>
#pragma comment(lib,"

MSWSOCK"

)
#include <windows.h> //这个头文件必须在winsock2.h之后,否则会出现各种编译错误
#include <string> //这个少不了的
#include <
fstream> //有可能会需要日志记录
#include <utility>
//C++实用,先添加进去看看,万一用到呢
#include <regex> //正则表达式,开发公司套件的时候用到,现在不一定能用到,先包含进来
#include <algorithm> //算法,想要写出高效的程序,这个目测必不可少,毕竟自己写的算法肯定比不上STL里面的,当然如果觉得自己之才不在那群C++的大牛之下,可以自己写算法
#include <vector> //这个想必都是必须的
#include <
functional>//这个东西很有用,我们会在里面给大家展示他的强大功能
#include <thread> //C++11线程库,我们用他来开多线程

#include <mutex> //C++11线程同步库,用他来同步


============================
ok,暂时这么多吧,不过给大家一个介意,不要在头文件里面包含一大堆头文件,可以前向声明的我们就前向声明,等到真正使用的时候我们再把相关的头文件包含进来就好,ok,我们继续进入主题,我们先把主要的功能封装起来,这是一个和网络有关的东西,我们就干脆叫MyNet吧,还是直接看声明吧:
========================================


namespace MyCode{
//自己编写的程序定义个命名空间是给好习惯,这是C++相对C的一个优势不用就是浪费

class MyNet;

//前向声明

const unsigned long MAX_BUF = 40960;



const unsigned long MAX_THREAD_COUNT = 32;

#define MAX_BU 40960

//如果不用上面的const定义常量的话,我们可以使用C的管用宏手法定义相关常量
#define MAX_THREAD_COUNT 32
//同上,不过,我喜欢const定义,就算是enum,我也不愿意用C的宏定义,所以,我在这里使用const定义常量


//这里定义一个枚举类型

enum OPTYPE{ RECV_POSTED = 0, SEND_POSTED, ACCEPT_POSTED ,IDLE_POSTED };



//此结构用于监听


struct SOCK_INFO{

SOCKET
_socket;

SOCKADDR_IN _addr;

}


//我们在这里封装一个结构体,我们要通信,主要还得靠这个结构体

//==========单IO数据结构=============

class PER_IO_DATA{

public:

PER_IO_DATA();

~PER_IO_DATA();


void ResetBuf();


void ReSet();

MyNet* GetMyNet();

WSAOVERLAPPED
_ol;

SOCKET

_socket;

SOCKADDR_IN
_addr;

WSABUF

_wsabuf;

char

_buf[MAX_BUF];

MyNet*

_pthis;

OPTYPE

_op;

private:

PER_IO_DATA(const PER_IO_DATA&

);

PER_IO_DATA&

operator=(const PER_IO_DATA&

);

};


//==============单句柄数据==============

class PER_HANDLE_DATA{

public:

PER_HANDLE_DATA();

~PER_HANDLE_DATA();


void SetSocket(SOCKET);

SOCKET&

GetSocket();


void SetAddr(SOCKADDR_IN&

);

SOCKADDR_IN&

GetAddr();


void SetName(std::string);

std::string&

GetName();


void AddCount();


void DecCount();

const unsigned long GetCount() const;

private:

std::string _Name;

SOCKET

_socket;

SOCKADDR_IN _addr;

unsigned long _Count;

};

//===================================


typedef std::function<void(PER_IO_DATA*, std::string&

)>OIFUN;

//消息处理函数,function在这里发挥了重要作用


//=============真正的网络库==============

class MyNet{

public:

MyNet();

~MyNet();


void CreatAndListen();

//创建socket和监


void SetFun(OIFUN);

//设置回调函数

const std::vector<std::string>&

GetUserInfo() const;

//获取用户信息

std::vector<std::string>&

GetUserInfo();

bool GetupdateUserInfo();


void SetupdateUserinfo(bool);

int SendMsg(PER_HANDLE_DATA*, std::string&

);

std::string RecvMsg(SOCKET&

);


void RecvMsg(PER_HANDLE_DATA*, PER_IO_DATA*);


public:

static
void CompletionThread(MyNet*,HANDLE);

std::vector<std::shared_ptr<PER_HANDLE_DATA>>&

GetUserVector();

MyNet* GetMyNet();


private:

MyNet(const MyNet&

);

MyNet&

operator=(const MyNet&

);

HANDLE
m_CompletionPort;

private:

SYSTEM_INFO
m_systeminfo;

std::shared_ptr<SOCK_INFO>
m_listensocket;

std::shared_ptr<PER_IO_DATA>m_PerIoData;

int
m_addrlen;

//地址长度

OIFUN m_fun;

//处理套接字信息

mutable bool

b_updateUser;

std::vector<std::thread> v_thread;

//线程池

std::mutex m_mtx;

std::recursive_mutex
m_rcmtx;

std::vector<std::shared_ptr<PER_HANDLE_DATA>> v_Userinfo;

//保存用户信息

std::vector<std::shared_ptr<PER_IO_DATA>> v_AcceptSocket;

LPFN_ACCEPTEX

lpfnAcceptEx;

LPFN_GETACCEPTEXSOCKADDRS

lpfnGetAcceptExSockaddrs;

LPFN_CONNECTEX

lpfnConnectEx;

private:


void init();

//初始化


void Accept(PER_IO_DATA*);

//用于接收连接请求


void HandleIo(PER_IO_DATA*);

//处理客户信息


void SendToAll(std::string&

);

//这个函数提供一个OP操作供完成端口捕获


void SendAllMsg(std::string);

//只是纯粹的转发消息


void SendMsg(std::shared_ptr<PER_HANDLE_DATA>&

, std::string&

);

//同上只是纯粹的转发消息


void CloseSocket(PER_HANDLE_DATA*);

//关闭已经断开连接的套接字信息


void pushUserName(PER_HANDLE_DATA*,PER_IO_DATA*);

//添加用户对象


void UpdateUser();

//更新用户列表


void HandleIo(PER_HANDLE_DATA*, PER_IO_DATA*, DWORD);

//处理完成端口捕获的信息

};

}

==================================
今天我们就大概的说一下这个类的相关功能,因为我们这是一个服务器,所以我们并没有设置ip和监听端口这些接口,作为服务器,我们可以将他指定为固定的端口和本机的ip,init初始化相关变量,CreatAndListen开启监听功能,只要开启监听,就可以接受客户端的连接请求,SendMsg和RecvMsg作为public不但是内部可以调用,外部以可以调用,Accept处理连接进来的客户端,HandleIo进一步处理客户信息,CompletionThread将会完成所有的消息发送,看到这里,大家可能觉得奇怪,这个类基本没有几个提供给外部使用的接口,那么想用这个类,怎么才能自己定制呢?答案在SetFun,我们只需要自己写一个函数,让该函数实现我们想要的功能就足够,然后将这个函数传给SetFun,于是我们实现的这个函数就成了一个回掉函数。好吧,今天就到这里,接下来我们再来实现该类。



=====================================

回复D或者d直接查看目录,回复相应的阿拉伯数字查看相应章节内容

原文始发于微信公众号(

C/C++的编程教室

):第八十六讲 综合运用(1)

|

发表评论