聊天服务器(MuduoManual.pdf P66)
examples/asio/chat/server.cc 单线程
examples/asio/chat/server_threaded.cc,多线程TcpServer,并用mutex来保护共享数据mutex
examples/asio/chat/server_threaded_efficient.cc,借shared_ptr实现copy-on-write的手法来降低锁竞争
examples/asio/chat/server_threaded_highperformance.cc,采用thread local变量实现多线程高效转发
消息分为包头与包体,每条消息有一个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字节
在传输层TCP 是字节流协议,应用层解析是否是一条完整的消息。
增加了一层间接层,codec( coder decoder) 就是消息编解码的意思 。
解决TCP粘包问题示例
void onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp receiveTime)
{
while (buf->readableBytes() >= kHeaderLen) // kHeaderLen == 4
{
// FIXME: use Buffer::peekInt32()
const void* data = buf->peek();
int32_t be32 = *static_cast<const int32_t*>(data); // SIGBUS
const int32_t len = muduo::net::sockets::networkToHost32(be32);
if (len > 65536 || len < 0)
{
LOG_ERROR << "Invalid length " << len;
conn->shutdown(); // FIXME: disable reading
break;
}
else if (buf->readableBytes() >= len + kHeaderLen)
{
buf->retrieve(kHeaderLen);
muduo::string message(buf->peek(), len);
messageCallback_(conn, message, receiveTime);
buf->retrieve(len);
}
else
{
break;
}
}
}
服务器如何处理错误的消息呢?
一条错误的消息:0x00,0x00,0x00,0x08, 'h','e','l','l','o'。可能是中途被修改,可以能是恶意的消息。
消息会带上一个应用层的校验信息的。比如说CRC32校验。如果校验错误,说明这条消息就是错误的消息。
第二种方式:服务器端应该有空闲断开功能。在一定时间没有收到客户端的消息,就断开它。
单线程聊天服务器
#include "examples/asio/chat/codec.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <set>
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
class ChatServer : noncopyable
{
public:
ChatServer(EventLoop* loop,
const InetAddress& listenAddr)
: server_(loop, listenAddr, "ChatServer"),
codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3))
{
server_.setConnectionCallback(
std::bind(&ChatServer::onConnection, this, _1));
server_.setMessag