blob: 087a24c063e4243a5ef9e51771e7f99e14bd17e8 [file] [log] [blame]
Alexander Afanasyeva9034b02014-01-26 18:32:02 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7#include "tcp-channel.hpp"
8
Alexander Afanasyev18bbf812014-01-29 01:40:23 -08009namespace nfd {
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080010
Alexander Afanasyev3958b012014-01-31 15:06:13 -080011NFD_LOG_INIT("TcpChannel");
12
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080013using namespace boost::asio;
14
15TcpChannel::TcpChannel(io_service& ioService,
16 const tcp::Endpoint& localEndpoint)
17 : m_ioService(ioService)
18 , m_localEndpoint(localEndpoint)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080019{
20}
21
22void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080023TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
24 const ConnectFailedCallback& onAcceptFailed,
25 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080026{
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080027 m_acceptor = make_shared<ip::tcp::acceptor>(boost::ref(m_ioService));
28 m_acceptor->open(m_localEndpoint.protocol());
29 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
30 m_acceptor->bind(m_localEndpoint);
31 m_acceptor->listen(backlog);
32
33 shared_ptr<ip::tcp::socket> clientSocket =
34 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
35 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080036 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080037 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080038 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080039}
40
41void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080042TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080043 const TcpChannel::FaceCreatedCallback& onFaceCreated,
44 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080045 const time::Duration& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080046{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080047 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
48 if (i != m_channelFaces.end()) {
49 onFaceCreated(i->second);
50 return;
51 }
52
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080053 shared_ptr<ip::tcp::socket> clientSocket =
54 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
55
56 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
57 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
58
Alexander Afanasyev3958b012014-01-31 15:06:13 -080059 clientSocket->open(m_localEndpoint.protocol());
60
61 // The following does not work and CCNx does not bind the local
62 // socket to a fixed port number for TCP connections
63
64 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
65 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080066
67 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080068 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
69 clientSocket, connectTimeoutTimer,
70 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080071
72 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080073 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
74 clientSocket, connectTimeoutTimer,
75 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080076}
77
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080078void
79TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080080 const TcpChannel::FaceCreatedCallback& onFaceCreated,
81 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080082 const time::Duration& timeout/* = time::seconds(4)*/)
83{
84 shared_ptr<ip::tcp::socket> clientSocket =
85 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
86
87 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
88 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
89
Alexander Afanasyev3958b012014-01-31 15:06:13 -080090 clientSocket->open(m_localEndpoint.protocol());
91
92 // The following does not work and CCNx does not bind the local
93 // socket to a fixed port number for TCP connections
94
95 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
96 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080097
98 ip::tcp::resolver::query query(remoteHost, remotePort);
99 shared_ptr<ip::tcp::resolver> resolver =
100 make_shared<ip::tcp::resolver>(boost::ref(m_ioService));
101
102 resolver->async_resolve(query,
103 bind(&TcpChannel::handleEndpointResoution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800104 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800105 onFaceCreated, onConnectFailed,
106 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800107
108 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800109 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
110 clientSocket, connectTimeoutTimer,
111 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800112}
113
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800114size_t
115TcpChannel::size() const
116{
117 return m_channelFaces.size();
118}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800119
120void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800121TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
122 const FaceCreatedCallback& onFaceCreated)
123{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800124 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800125
126 shared_ptr<TcpFace> face = make_shared<TcpFace>(boost::cref(socket));
127 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
128
129 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800130 m_channelFaces[remoteEndpoint] = face;
131}
132
133void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800134TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
135{
136 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
137 m_channelFaces.erase(remoteEndpoint);
138}
139
140void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800141TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
142 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
143 const FaceCreatedCallback& onFaceCreated,
144 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800145{
146 if (error) {
147 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
148 return;
149
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800150 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
151 << error.category().message(error.value()));
152
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800153 onAcceptFailed("Connect to remote endpoint failed: " +
154 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800155 return;
156 }
157
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800158 // prepare accepting the next connection
159 shared_ptr<ip::tcp::socket> clientSocket =
160 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
161 m_acceptor->async_accept(*clientSocket,
162 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
163 clientSocket,
164 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800165
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800166 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
167 "<< Connection from " << socket->remote_endpoint());
168 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800169}
170
171void
172TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
173 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800174 const shared_ptr<monotonic_deadline_timer>& timer,
175 const FaceCreatedCallback& onFaceCreated,
176 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800177{
178 timer->cancel();
179
180 if (error) {
181 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
182 return;
183
184 socket->close();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800185
186 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
187 << error.category().message(error.value()));
188
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800189 onConnectFailed("Connect to remote endpoint failed: " +
190 error.category().message(error.value()));
191 return;
192 }
193
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800194 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
195 ">> Connection to " << socket->remote_endpoint());
196
197 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800198}
199
200void
201TcpChannel::handleFailedConnect(const boost::system::error_code& error,
202 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800203 const shared_ptr<monotonic_deadline_timer>& timer,
204 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800205{
206 if (error) { // e.g., cancelled
207 return;
208 }
209
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800210 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
211 << error.category().message(error.value()));
212
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800213 onConnectFailed("Connect to remote endpoint timed out: " +
214 error.category().message(error.value()));
215 socket->close(); // abort the connection
216}
217
218void
219TcpChannel::handleEndpointResoution(const boost::system::error_code& error,
220 ip::tcp::resolver::iterator remoteEndpoint,
221 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800222 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
223 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800224 const ConnectFailedCallback& onConnectFailed,
225 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800226{
227 if (error ||
228 remoteEndpoint == ip::tcp::resolver::iterator())
229 {
230 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
231 return;
232
233 socket->close();
234 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800235
236 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
237 << error.category().message(error.value()));
238
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800239 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
240 error.category().message(error.value()));
241 return;
242 }
243
244 // got endpoint, now trying to connect (only try the first resolution option)
245 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800246 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
247 socket, timer,
248 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800249}
250
251
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800252} // namespace nfd