当前位置 博文首页 > wanggao的专栏:muduo学习笔记:net部分之实现TCP网络编程库-Tcp
有了前文【muduo学习笔记:net部分之实现TCP网络编程库-Connector】的介绍,TcpClient的实现就不难了。muduo用TcpClient发起连接,TcpClient有一个Connector连接器,TCPClient使用Conneccor发起连接, 连接建立成功后, 用socket创建TcpConnection来管理连接, 每个TcpClient class只管理一个TcpConnecction,连接建立成功后设置相应的回调函数。很显然,TcpClient用来管理客户端连接,真正连接交给Connector。
它的代码与TcpServer甚至有几分相似,只不过TcpClient只管理一个TcpConnection。先谈几个要点:
TcpClient使用Conneccor发起连接, 连接建立成功后,用socket创建TcpConnection来管理连接, 每个TcpClient class只管理一个TcpConnecction。
class TcpClient : noncopyable
{
public:
// TcpClient(EventLoop* loop);
// TcpClient(EventLoop* loop, const string& host, uint16_t port);
TcpClient(EventLoop* loop,
const InetAddress& serverAddr,
const string& nameArg);
~TcpClient(); // force out-line dtor, for std::unique_ptr members.
void connect();
void disconnect();
void stop();
TcpConnectionPtr connection() const
{
MutexLockGuard lock(mutex_);
return connection_;
}
EventLoop* getLoop() const { return loop_; }
bool retry() const { return retry_; }
void enableRetry() { retry_ = true; }
const string& name() const { return name_; }
/// Set connection callback.
/// Not thread safe.
void setConnectionCallback(ConnectionCallback cb) { connectionCallback_ = std::move(cb); }
/// Set message callback.
/// Not thread safe.
void setMessageCallback(MessageCallback cb) { messageCallback_ = std::move(cb); }
/// Set write complete callback.
/// Not thread safe.
void setWriteCompleteCallback(WriteCompleteCallback cb) { writeCompleteCallback_ = std::move(cb); }
private:
/// Not thread safe, but in loop
void newConnection(int sockfd);
/// Not thread safe, but in loop
void removeConnection(const TcpConnectionPtr& conn);
EventLoop* loop_; // 所属的EvenetLoop
ConnectorPtr connector_; // 使用Connector智能指针,避免头文件引入
const string name_; // 连接的名字
ConnectionCallback connectionCallback_; // 建立连接的回调函数
MessageCallback messageCallback_; // 消息到来的回调函数
WriteCompleteCallback writeCompleteCallback_; // 数据发送完毕回调函数
bool retry_; // atomic // 连接断开后是否重连
bool connect_; // atomic
// always in loop thread
int nextConnId_; // name_+nextConnId_ 用于标识一个连接
mutable MutexLock mutex_;
TcpConnectionPtr connection_ GUARDED_BY(mutex_);
};
构造TcpClient时,初始化Connector并注册事件回调,用于与服务端的通信,并设置连接成功的回调函数。
初始化列表种创建一个Connector,用于连接服务器,若连接成功,调用TcpClient::newConnection()回调函数。
TcpClient::TcpClient(EventLoop* loop,
const InetAddress& serverAddr,
const string& nameArg)
: loop_(CHECK_NOTNULL(loop)),
connector_(new Connector(loop, serverAddr)), // 创建一个Connector
name_(nameArg),
connectionCallback_(defaultConnectionCallback),
messageCallback_(defaultMessageCallback),
retry_(false), // 默认不重连
connect_(true), // 开始连接
nextConnId_(1) // 当前连接的序号
{
// 设置建立连接的回调函数
connector_->setNewConnectionCallback(std::bind(&TcpClient::newConnection, this, _1));
// FIXME setConnectFailedCallback
LOG_INFO << "TcpClient::TcpClient[" << name_ << "] - connector " << get_pointer(connector_);
}
connect()函数调用Connector::start(),发起连接。
void TcpClient::connect()
{
// FIXME: check state
LOG_INFO << "TcpClient::connect[" << name_ << "] - connecting to " << connector_->serverAddress().toIpPort();
connect_ = true;
connector_->start(); // 调用Connector::start,发起连接
}
若连接成功,则回调TcpClient::newConnection()函数。 参数是本地已建立连接的sockfd,通过它创建一个TcpConnection,用于后续消息的发送。
void TcpClient::newConnection(int sockfd)
{
loop_->assertInLoopThread();
InetAddress peerAddr(sockets::getPeerAddr(sockfd)); // 获取对端的地址
char buf[32];
snprintf(buf, sizeof buf, ":%s#%d", peerAddr.toIpPort().c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf; // 连接的名字
InetAddress localAddr(sockets::getLocalAddr(sockfd)); // 获取本段的地址
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
// 构造一个TcpConnection对象,并设置相应的回调函数
TcpConnectionPtr conn(new TcpConnection(loop_,
connName,
sockfd,
localAddr,
peerAddr));
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, _1)); // FIXME: unsafe
{
MutexLockGuard lock(mutex_);
connection_ = conn; // 保存到成员变量
}
conn->connectEstablished(); // 注册到Poller,监听IO事件
}
断开连接,仅关闭写功能,仍然能接收对端消息。
void TcpClient::disconnect()
{
connect_ = false;
{
MutexLockGuard lock(mutex_);
if (connection_) {
connection_->shutdown(); // 半关闭, 能继续完整接收对端的消息
}
}
}
关闭连接,则将完全关闭客户端,不能再进行收、发数据。
void TcpClient::stop()
{
connect_ = false;
connector_->stop();
}
TcpClient::~TcpClient()
{
LOG_INFO << "TcpClient::~TcpClient[" << name_ << "] - connector " << get_pointer(connector_);
TcpConnectionPtr conn;
bool unique = false;
{
MutexLockGuard lock(mutex_);
unique = connection_.unique(); // 是否只有一个持有者
conn = connection_;
}
if (conn) // 连接已经建立成功,TcpConnectionPtr 不为空
{
assert(loop_ == conn->getLoop());
// FIXME: not 100% safe, if we are in different thread
CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);
loop_->runInLoop(std::bind(&TcpConnection::setCloseCallback, conn, cb));
if (unique){
conn->forceClose();
}
}
else{ // TcpConnectionPtr为空
connector_->stop(); // 关闭Connector连接
// FIXME: HACK
loop_->runAfter(1, std::bind(&detail::removeConnector, connector_));
}
}
EchoClient
#include <muduo/net/TcpClient.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <utility>
#include <stdio.h>
#include <unistd.h>
using namespace muduo;