从零构建通讯器--6.7专门创建一个线程统一负责数据发送

目录:
在这里插入图片描述

(1)发送数据指导思想

把要发送的数据放到一个发送队列中[msgSend],然后咱们专门创建一个线程[ServerSendQueueThread]来统一负责数据发送;(把太多的事情放到主线程中不是太好的主意)

(2)发送数据代码实战

1)在ngx_c_socket.cxx中的msgSend将一个发送消息入到消息队列中
2)多线程操作这个队列:可能存在多个线程同时来处理发送队列的问题,例如送消息到消息队列和从消息队列取消息,所以必须要互斥线程的操作(CLock自动互斥,自动释放)
在这里插入图片描述
3)发消息队列时做下计数,这是个原子操作(用原子操作也许更省效率,因为容器.size()自增可能有操作代价),其中++才是原子操作,+=不是原子操作
在这里插入图片描述
4)sem_post()
信号量定义
在这里插入图片描述
①用之前,在函数Ngx_c_socket.cxx的Initialize()中用sem_init初始化信号量
第一个参数为 &信号量名 <<–取信号量的地址
第二个参数为 0,表示信号量在线程间共享;如果非0表示在进程间共享
第三个参数为 0,表示信号量的初始值为0,为0时,调用sem_wait()就会卡在那里
②用完后再使用sem_destroy()释放信号量(在ngx_c_socket.cxx中的shutdown_subproc())
③本项目就在sem_wait()中用到了信号量,另外一个在sem_wait()用到了信号量
sem_wait(): 测试指定信号量的值,如果该值>0,那么该值-1然后该函数返回,直接向下运行;若该值等于0,那么该线程将投入睡眠,直到该值>0,然后该值-1往下运行,不再卡住
sem_post() 将指定信号量+1,即使当前没有其他线程在等待该信号量值也没关系
④在写线程池时(misc文件里面的ngx_c_threadpool.cxx),pthread_cond_signal()用来唤醒卡在pthread_cond_wait这里的线程,若调用的时候没有线程卡在pthread_cond_wait,那么就相当于白调用了pthread_cond_signal()
⑤在msgSend()中用sem_post()将信号量+1,为了让后面的线程干活

(2.1)信号量

信号量也是(线程间)的同步机制,跟互斥量的不同在于:
互斥量-》线程之间同步
信号量-》提供进程之间同步也能提供线程之间的同步
这里使用的是线程之间的同步,相对于互斥量更容易理解-》》推荐《Unix网络编程 卷2-进程间通讯》第二版,第十章 清晰描述了信号量用法

—(2.2)数据发送线程 ServerSendQueueThread 很重要*************

在这里插入图片描述
1)在ngx_c_socket.cxx的Initialize_subproc()中初始化这个发送数据的线程
在这里插入图片描述
2)处理发送消息队列的线程 <<–核心代码
在这里插入图片描述
①找到Csocket对象的指针在这里插入图片描述
②若原子数大于0,表示有消息的要发送,遍历消息队列,while全部发送出去
③以收到客户端发来数据头的序号判断,客户端是否断开连接
(若客户端断开连接,这个连接放到回收队列中,这个序号会加1或这个连接池中这个连接被回收,这个序号会被加1),不相等表示连接已经断开了在这里插入图片描述
a)迭代器往后走一个 pos++
b)真正清队列的时候清的是pos2
c)(原子的)发消息队列–
d)释放掉相关内存
e)continue走到下一个消息队列元素
④刚开始计数为0,大于0表示缓冲区种数据正在发送,且没发送完,就continue
例子:如果张三有两条数据,第一条还没收,第二条就已经准备好要发,但是得按顺序来,所以要continue等第一条信息发完(靠系统驱动发消息)在这里插入图片描述
⑤发送消息队列容量减1,保存要释放的内存,发送包头+包体的结构体
用sendproc发送数据到缓冲区
与6.6后面的第二种方法相关
当发送缓冲区满了,++p_Conn->iThrowsendCout表示发送缓冲区满了,要把写事件加入epoll,,让epoll驱动我们继续发送数据,把可写事件加入epoll

a)若发送了0字节,表示socket断了,就释放内存就完事,收消息再处理多余的事情;
b)如果sendsize==-1,表示当前缓冲区满了,在epoll种增加可写通知;
c)如果errno==EINTR也不算错误,可能是收到某个信号导致send产生这个错误,按nginx官方的做法,就打印个日志也不需要做啥,返回for循环继续发送;
d)若返回-2表示对端断开,操作系统会把我们把节点从红黑树种移除,我们自己需要释放内存,将ThrowsendCout置为0
⑦走下来把临界资源释放掉

(2.3)可写通知到达后数据的继续发送 很重要*********************
在这里插入图片描述
接受的是可写事件
在这里插入图片描述
开始发送数据
在这里插入图片描述
发送成功就把写事件从epoll种去除
—(2.4)发送数据的简单测试
MFC种新增RecvCommonData函数收数据
//发送缓冲区大概10-几10K,
//如何把发送缓冲区撑满
//(1)每次服务器给客户端发送65K左右的数据,发送到第20次才出现服务器的发送缓冲区满;这时客户端收了一个包(65K),
//此时执行了 ngx_write_request_handler();
//(2)我又发包,连续成功发送了16次,才又出现发送缓冲区满;我客户端再收包,结果连续收了16次包,服务器才又出现
//ngx_write_request_handler()函数被成功执行,这表示客户端连续收了16次包,服务器的发送缓冲区才倒出地方来;
(客户端点击收包按钮才收包)
//(3)此后,大概服务器能够连续发送16次才再出现发送缓冲区满,客户端连续收16次,服务器端才出现ngx_write_request_handler()被执行【服务器的发送缓冲区有地方】;

//测试结论:
//(1)ngx_write_request_handler()逻辑正确;能够通过此函数把剩余的未成功发送的数据发送出去;
//(2)LT模式下,我们发送数据采用的 改进方案 是非常有效的,在很大程度上提高了效率;
//(3) 发送缓冲区大概10-几10K,但是我们实际测试的时候,成功的发送出去了1000多k数据才报告发送缓冲区满;
//当我们发送端调用send()发送数据时,操作系统底层已经把数据发送到了 该连接的接收端 的接收缓存,这个接收缓存大概有几百K,

**千万不要认为发送缓冲区只有几十K,所以我们send()几十k就能把发送缓冲区填满**

//(4)不管怎么说,主要对方不接收数据,发送方的发送缓冲区总有满的时候;
 //当发送缓冲满的时候,我们发送数据就会使用ngx_write_request_handler()来执行了,所以现在看起来,我们整个的服务器的发送数据的实现代码是正确的;

在这里插入图片描述

(3)发送数据后续处理代码

若发送给数据过程中,客户端断开,超过垃圾时钟, 内存会回收回来
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值