这里简要分析TCPConnection的用法以及代码
1. TcpConnection 发送
2. TcpConnection 接收
TcpConnection::handleRead调用
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
一次把内核缓冲区的数据读完
3 TcpConnection 一些小点
3.1 TcpConnection 接收设计原理
TCP协议的shutdown与close等描述
上个文章解释了为什么muduo的TCP连接中,使用shutdown后调用read() == 0来判断对端是否关闭写,因为发送FIN之后,接收方会read返回0。非阻塞read没有数据返回-1。
这里有一点需要注意,socket::shutdown()本身只会关闭连接,不会关闭socket fd,TCPConnection有一个 组合的socket对象,std::unique_ptr< Socket> socket_;而socket对象RAII的析构函数中有 sockets::close(sockfd_);
3.2 TcpConnection 存储变量
TcpConnection 中,可以使用使用boost::any与boost::any_cast实现任意类型的数据存储与提取,muduo中例子实现对conn中的FILE*指针的存储与操作
boost::any context_;
void setContext(const boost::any& context)
{ context_ = context; }
const boost::any& getContext() const
{ return context_; }
注意问题
3.3 TcpConnection 中一个简单的shared_ptr
这是muduo的一个简单的tcp连接,但是通过shared_ptr来管理FILE*的生命周期,使其运用了C++的RAII,生命周期与TCPConnection一致。同时节省了很多代码。
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
void onHighWaterMark(const TcpConnectionPtr& conn, size_t len)
{
LOG_INFO << "HighWaterMark " << len;
}
const int kBufSize = 64*1024;
const char* g_file = NULL;
typedef std::shared_ptr<FILE> FilePtr; //line3
void onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
LOG_INFO << "FileServer - Sending file " << g_file
<< " to " << conn->peerAddress().toIpPort();
conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1);
FILE* fp = ::fopen(g_file, "rb"); // line1
if (fp)
{
FilePtr ctx(fp, ::fclose); //line2
conn->setContext(ctx);
char buf[kBufSize];
size_t nread = ::fread(buf, 1, sizeof buf, fp);
conn->send(buf, static_cast<int>(nread));
}
else
{
conn->shutdown();
LOG_INFO << "FileServer - no such file";
}
}
}
void onWriteComplete(const TcpConnectionPtr& conn)
{
const FilePtr& fp = boost::any_cast<const FilePtr&>(conn->getContext());
char buf[kBufSize];
size_t nread = ::fread(buf, 1, sizeof buf, get_pointer(fp));
if (nread > 0)
{
conn->send(buf, static_cast<int>(nread));
}
else
{
conn->shutdown();
LOG_INFO << "FileServer - done";
}
}
int main(int argc, char* argv[])
{
LOG_INFO << "pid = " << getpid();
if (argc > 1)
{
g_file = argv[1];
EventLoop loop;
InetAddress listenAddr(2021);
TcpServer server(&loop, listenAddr, "FileServer");
server.setConnectionCallback(onConnection);
server.setWriteCompleteCallback(onWriteComplete);
server.start();
loop.loop();
}
else
{
fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
}
}
如果不用shared_ptr,则如下
void onConnection(const TcpConnectionPtr& conn)
if...
else
{
if (!conn->getContext().empty())
{
FILE* fp = boost::any_cast<FILE*>(conn->getContext());
if (fp)
{
::fclose(fp);
}
}
}