1// excerpts from http://code.google.com/p/muduo/
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the License file.
5//
6// Author: Shuo Chen (chenshuo at chenshuo dot com)
7
8#include "TcpClient.h"
9
10#include "Connector.h"
11#include "EventLoop.h"
12#include "SocketsOps.h"
13
14#include "logging/Logging.h"
15
16#include <boost/bind.hpp>
17
18#include <stdio.h>  // snprintf
19
20using namespace muduo;
21
22// TcpClient::TcpClient(EventLoop* loop)
23//   : loop_(loop)
24// {
25// }
26
27// TcpClient::TcpClient(EventLoop* loop, const string& host, uint16_t port)
28//   : loop_(CHECK_NOTNULL(loop)),
29//     serverAddr_(host, port)
30// {
31// }
32
33namespace muduo
34{
35namespace detail
36{
37
38void removeConnection(EventLoop* loop, const TcpConnectionPtr& conn)
39{
40  loop->queueInLoop(boost::bind(&TcpConnection::connectDestroyed, conn));
41}
42
43void removeConnector(const ConnectorPtr& connector)
44{
45  //connector->
46}
47
48}
49}
50
51TcpClient::TcpClient(EventLoop* loop,
52                     const InetAddress& serverAddr)
53  : loop_(CHECK_NOTNULL(loop)),
54    connector_(new Connector(loop, serverAddr)),
55    retry_(false),
56    connect_(true),
57    nextConnId_(1)
58{
59  connector_->setNewConnectionCallback(
60      boost::bind(&TcpClient::newConnection, this, _1));
61  // FIXME setConnectFailedCallback
62  LOG_INFO << "TcpClient::TcpClient[" << this
63           << "] - connector " << get_pointer(connector_);
64}
65
66TcpClient::~TcpClient()
67{
68  LOG_INFO << "TcpClient::~TcpClient[" << this
69           << "] - connector " << get_pointer(connector_);
70  TcpConnectionPtr conn;
71  {
72    MutexLockGuard lock(mutex_);
73    conn = connection_;
74  }
75  if (conn)
76  {
77    // FIXME: not 100% safe, if we are in different thread
78    CloseCallback cb = boost::bind(&detail::removeConnection, loop_, _1);
79    loop_->runInLoop(
80        boost::bind(&TcpConnection::setCloseCallback, conn, cb));
81  }
82  else
83  {
84    connector_->stop();
85    // FIXME: HACK
86    loop_->runAfter(1, boost::bind(&detail::removeConnector, connector_));
87  }
88}
89
90void TcpClient::connect()
91{
92  // FIXME: check state
93  LOG_INFO << "TcpClient::connect[" << this << "] - connecting to "
94           << connector_->serverAddress().toHostPort();
95  connect_ = true;
96  connector_->start();
97}
98
99void TcpClient::disconnect()
100{
101  connect_ = false;
102
103  {
104    MutexLockGuard lock(mutex_);
105    if (connection_)
106    {
107      connection_->shutdown();
108    }
109  }
110}
111
112void TcpClient::stop()
113{
114  connect_ = false;
115  connector_->stop();
116}
117
118void TcpClient::newConnection(int sockfd)
119{
120  loop_->assertInLoopThread();
121  InetAddress peerAddr(sockets::getPeerAddr(sockfd));
122  char buf[32];
123  snprintf(buf, sizeof buf, ":%s#%d", peerAddr.toHostPort().c_str(), nextConnId_);
124  ++nextConnId_;
125  string connName = buf;
126
127  InetAddress localAddr(sockets::getLocalAddr(sockfd));
128  // FIXME poll with zero timeout to double confirm the new connection
129  // FIXME use make_shared if necessary
130  TcpConnectionPtr conn(new TcpConnection(loop_,
131                                          connName,
132                                          sockfd,
133                                          localAddr,
134                                          peerAddr));
135
136  conn->setConnectionCallback(connectionCallback_);
137  conn->setMessageCallback(messageCallback_);
138  conn->setWriteCompleteCallback(writeCompleteCallback_);
139  conn->setCloseCallback(
140      boost::bind(&TcpClient::removeConnection, this, _1)); // FIXME: unsafe
141  {
142    MutexLockGuard lock(mutex_);
143    connection_ = conn;
144  }
145  conn->connectEstablished();
146}
147
148void TcpClient::removeConnection(const TcpConnectionPtr& conn)
149{
150  loop_->assertInLoopThread();
151  assert(loop_ == conn->getLoop());
152
153  {
154    MutexLockGuard lock(mutex_);
155    assert(connection_ == conn);
156    connection_.reset();
157  }
158
159  loop_->queueInLoop(boost::bind(&TcpConnection::connectDestroyed, conn));
160  if (retry_ && connect_)
161  {
162    LOG_INFO << "TcpClient::connect[" << this << "] - Reconnecting to "
163             << connector_->serverAddress().toHostPort();
164    connector_->restart();
165  }
166}
167
168