TcpConnection
这个类就是用来帮助我们管理连接的。
在最开始之前,仅仅凭照之前的一些使用经验,想想需要哪些东西。
- connfd。这个会被我们抽象成一个Handler。
- 应用层缓冲区。为什么需要应用层缓冲区?——请查看muduo手册《Muduo Manual》中关于应用层缓冲区的设定。
- 收和接
我们希望当connHandler就绪的时候,自动去回调我们设置给它的回调函数——handleRead
handleRead
Buffer需要帮我们干了什么?
当connHandler就绪的时候,我们的网络库,也就是Buffer需要将数据从内核缓冲区安全
地读出来。(这个安全
即不会漏读,多读和错读)
这时根据read之后的返回值,去回调对应的回调函数。
handleRead(connfd)
{
int n = buffer.readFd(connfd);
if(n > 0)
{
messageCallback(...);
}
else{
closeCallback(...);
}
}
如果n > 0。回调我们的messageCallback。这是通过用户自己编写的,TcpServer->setMessageCallback,再去调用TcpConnection->setMessageCallback。我们在用户自己编写的messageCallback中。将数据从buffer中读取出来。然后处理。然后将数据需要重新发回到应用层发送缓冲区中。因此这里涉及TcpConnection->send的设计。之后谈。
如果n <= 0。代表出错,或者对端断开连接。我们这里当前统一的以断开连接去处理。此时需要我们去回调closeCallback。此时涉及连接的断开。这里也是一个大重点,后面会重新拿出来一篇文章来谈。
send
当我们调用send的时候,我们希望我们所在的应用层可以不去care网络层是否将数据发送完毕。我们只需要将数据发送到应用层缓冲区即可。至于底层,希望它可以自己处理好。
以上实际上是应用层缓冲区设计的核心思想。即我们仅仅只和应用层之间的数据打交道。而不再关心网络层或者内核缓冲区中的数据。
send的伪代码
TcpConnection::send(char *data, int size)
{
// step1: 尝试直接发送
int n = write(connfd, data, size);
// 如果n == size 则代表我们全部发送内核缓冲区中。完成了任务
// 如果n < size 则代表我们有 size - n 的数据没有发送到内核缓冲区中。(不管什么原因)
if(n < 0)
handlerError();
if(n < size)
{ // 将剩余数据存放到应用层缓冲区中
char *tmp = data + n;
outputBuffer.append(tmp, size - data);
// 然后向reactor注册一个写事件
handler->setWriteCallback(handleWrite);
handler->enableWrite();
reactor->update(handler);
}
}
handleWrite
handleWrite里面的逻辑很简单,就是将应用层写缓冲区的数据取出来,发送到内核写缓冲区中去。如果取完了,就在reactor中取消关注写事件。