#技术总结
1、修改进程名
原理:修改命令行参数的第一个参数的名字,需要移动环境变量的存储地址。
框架总结
通讯框架
通讯框架采用多进程的设计方法,一个master进程,多个worker进程。
master进程:
1、等待信号并处理信号。
2、负责生成和管理worker进程,当worker进程死掉,则重启worker进程。
3、日志文件类的初始化
单例类实现,类中套类,用于析构对象。类负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。用到静态成员函数,和静态数据成员。构造函数是私有的。
初始化的时候打开文件描述符,读取文件中的配置项和值放在vector<结构体>中。
4、配置文件类的初始化
5、全局变量的初始化
6、信号的初始化
7、socket的初始化
8、创建守护进程
9、释放资源
10、环境变量搬家
worker进程:
1、子进程初始化
一、清空屏蔽的信号集,不屏蔽信号。创建线程池,配置项配置的是每个子进程要创建的线程数量。
创建线程并使所有线程卡在pthred_cond_wait()函数中。
此时主线程返回继续执行。
二、Initialize_subproc()初始化一些互斥量,信号量,创建用来发数据的线程,创建用来回收连接的线程,创建专门用来处理到期不发心跳包的用户踢出的线程。
三、ngx_epoll_init()初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
*创建epoll对象
*初始化连接池。初始分配ngx_connection_t*m_worker_connections那么大的内存。放到m_connectionList和m_freeconnectionList连接队列中。
*每个监听socket增加一个 连接池中的连接,和一个内存绑定
*为监听端口增加监听事件,以在后面有连接过来的时候能执行接收函数
四、进入死循环,执行ngx_process_events_and_timers进而调用ngx_epoll_process_events中执行epoll_wait()等待事件的到来,此时epoll_wait()是非阻塞的,因为listenid是非阻塞的。如果是监听描述符的读事件则调用ngx_event_accept处理,如果是连接的描述符读事件则调用ngx_read_request_handler处理,写事件则调用&CSocekt::ngx_write_request_handler处理。
五、连接描述符的读事件处理
这里涉及到包的接收方式,收包的过程为:这里epoll采用LT的工作模式,每次接收buflen长度的数据到缓冲区buff。epoll_wait不断返回,recv不断接收。。先接收包头,接收完后执行包头处理函数ngx_wait_request_handler_proc_p1(),包头1字节对齐,先收包头,在包头中找到包体长度的位置,再收包体。
我原来在想一个问题,如果客户端一个包发送恶意包过来,是否后面一直在判断包头的状态?
后来想想没大问题,因为收包只是针对某个连接收包,收到恶意包也不会影响其他的连接。最多就把这个连接关闭掉。
状态机
设置4个状态:接收包头准备,收包头中,包体准备,收包体中。
入消息队列
inMsgRecvQueueAndSignal并唤醒一个等待条件的线程(主线程收包,入消息队列,线程池处理消息队列)
m_MsgRecvQueue消息队列是一个char *的LIST。
业务框架
取消息队列中的消息
pThreadPoolObj->m_MsgRecvQueue.front();
并移除消息pop_front();,只移除指针。
处理消息
g_socket.threadRecvProcFunc(),继承CSOCKET类的对应函数。
先进行crc32判断。
再根据消息代码pPkgHeader->msgCode执行不同的处理函数。定义一个保存成员函数指针的数组,根据下标找到对应的函数,效率特别高。
处理具体的业务逻辑
CLogicSocket::_HandleRegister
返回数据给客户端
发送消息队列:m_MsgSendQueue.push_back(psendbuf);
发送队列过大,直接干掉一部分数据。
客户端不接收数据,切断连接。
此处使用信号量同步。
注意:
1、控制连接数,不能无限连接。统计在线用户数。
2、收到太多报处理不过来要降速,移除epoll感兴趣的该连接的读事件。
3、引入心跳包,防止客户端网线断了而无感。
4、SO_REUSEADDR选项使time_wait也可以重新启动程序。
网友评论