(P44-P45)muduo库使用例子(三):聊天服务器

1.聊天服务器

  • 聊天服务器(MuduoManual.pdf P66)
    (1)examples/asio/chat/server.cc 单线程
    (2)examples/asio/chat/server_threaded.cc,多线程TcpServer,并用mutex来保护共享数据mutex,由于mutex的存在,多线程并不能并发执行
    (3)examples/asio/chat/server_threaded_efficient.cc,借shared_ptr实现copy-on-write的手法来降低锁竞争
    (4)examples/asio/chat/server_threaded_highperformance.cc,采用thread local变量实现多线程高效转发

  • 聊天服务器示意图
    服务端收到客户端的hello消息,服务端转发hello消息到C1,C2,C3客户端
    在这里插入图片描述

2.消息格式

  • 消息分为包头与包体,每条消息有一个4字节的头部,以网络序存放字符串的长度。包体是一个字符串,字符串也不一定以’\0’结尾。比方说有两条消息"hello"和"chenshuo",那么打包后的字节流是:
  • 0x00,0x00,0x00,0x05, ‘h’,‘e’,‘l’,‘l’,‘o’,0x00,0x00,0x00,0x08,‘c’,‘h’, ‘e’,‘n’,‘s’,‘h’,‘u’,‘o’
    共21字节。
    对于传输层来讲这是一个字节流,是没有边界的。在应用层将其解析出来,解析成2条消息,先解析包头,再解析包体,这才是一条完整的消息

3.时序图

  • coder decoder组合以下就是codec编解码,前者取前2个字符,后者取前3个字符;
    LengthHeaderCodec就是对聊天服务器的消息进行编解码;
    (1)当可读事件到来,回调LengthHeaderCodec中的onMessage,然后对收到的消息进行解码decode,先解析包头,再解析包尾,然后再把包体的字符串传给ChatServer,回调onStringMessage;
    ()ChatServer收到消息,将其转发给在线的客户端,调用LengthHeaderCodec::send(),因为要进行编码(包头+包体),send(Buffer)发送给在线的客户端
    在这里插入图片描述

  • eg:44\jmuduo\examples\asio\chat\server.cc----------单线程版本
    44\jmuduo\examples\asio\chat\codec.h
    44\jmuduo\examples\asio\chat\client.cc
    44\jmuduo\examples\asio\chat\server_threaded.cc----------多线程版本

  • eg测试:单线程:44\jmuduo\examples\asio\chat\server.cc
    44\jmuduo\examples\asio\chat\client.cc

  • 测试:服务端
    在这里插入图片描述
    客户端1
    在这里插入图片描述
    在这里插入图片描述
    客户端2
    在这里插入图片描述
    在这里插入图片描述

4.通过编程技巧降低锁竞争和提高并发效率

  • 问题:45\jmuduo\examples\asio\chat\server_threaded.cc,多线程TcpServer,并用mutex来保护共享数据mutex,由于mutex的存在,多线程并不能并发执行,而是串行的。因而存在较高的锁竞争,效率比较低。
    (1)eg:C1向服务器端发送一条消息hello,服务器通过一个IO线程转发给所有的客户端,与此同时C2向服务器端发送一条消息hello2,服务器端通过另一个IO线程转发给所有客户端,由于锁的存在,这两个线程并不能并发执行,而是串行的。这个时候,客户端数目比较大,第二条消息hello2到达各个客户端的延迟也比较大。
    (2)大并发的一个杀手就是锁竞争

  • examples/asio/chat/server_threaded_efficient.cc,借shared_ptr实现copy-on-write的手法来降低锁竞争
    (1)shared_ptr是引用计数智能指针,如果当前只有一个观察者,那么引用计数为1,可以用shared_ptr::unique()来判断(为真引用计数为1,否则不为1;shared_ptr共享资源既有读端,也有写端)
    (2)对于write端,如果发现引用计数为1(只有一个写者来访问它),这时可以安全地修改对象,不必担心有人在读它。
    (3)对于read端,在读之前把引用计数加1,读完之后减1,这样可以保证在读的期间其引用计数大于1,可以阻止并发写。
    (4)比较难的是,对于write端,如果发现引用计数大于1,该如何处理?既然要更新数据,肯定要加锁,如果这时候其他线程正在读,那么不能在原来的数据上修改,得创建一个副本,在副本上修改,修改完了再替换。如果没有用户在读,那么可以直接修改。

  • eg:45\jmuduo\examples\asio\chat\server_threaded_efficient.cc
    45\jmuduo\examples\asio\chat\server_threaded_highperformance.cc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值