blob: 026c338f51ceaf1326c496e168e0a7de32d1f1ef [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
114
115void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800116TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
117 const FaceCreatedCallback& onFaceCreated)
118{
119 shared_ptr<TcpFace> face = make_shared<TcpFace>(boost::cref(socket));
120 onFaceCreated(face);
121
122 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
123 m_channelFaces[remoteEndpoint] = face;
124}
125
126void
127TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
128 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
129 const FaceCreatedCallback& onFaceCreated,
130 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800131{
132 if (error) {
133 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
134 return;
135
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800136 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
137 << error.category().message(error.value()));
138
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800139 onAcceptFailed("Connect to remote endpoint failed: " +
140 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800141 return;
142 }
143
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800144 // prepare accepting the next connection
145 shared_ptr<ip::tcp::socket> clientSocket =
146 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
147 m_acceptor->async_accept(*clientSocket,
148 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
149 clientSocket,
150 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800151
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800152 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
153 "<< Connection from " << socket->remote_endpoint());
154 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800155}
156
157void
158TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
159 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800160 const shared_ptr<monotonic_deadline_timer>& timer,
161 const FaceCreatedCallback& onFaceCreated,
162 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800163{
164 timer->cancel();
165
166 if (error) {
167 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
168 return;
169
170 socket->close();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800171
172 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
173 << error.category().message(error.value()));
174
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800175 onConnectFailed("Connect to remote endpoint failed: " +
176 error.category().message(error.value()));
177 return;
178 }
179
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800180 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
181 ">> Connection to " << socket->remote_endpoint());
182
183 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800184}
185
186void
187TcpChannel::handleFailedConnect(const boost::system::error_code& error,
188 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800189 const shared_ptr<monotonic_deadline_timer>& timer,
190 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800191{
192 if (error) { // e.g., cancelled
193 return;
194 }
195
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800196 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
197 << error.category().message(error.value()));
198
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800199 onConnectFailed("Connect to remote endpoint timed out: " +
200 error.category().message(error.value()));
201 socket->close(); // abort the connection
202}
203
204void
205TcpChannel::handleEndpointResoution(const boost::system::error_code& error,
206 ip::tcp::resolver::iterator remoteEndpoint,
207 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800208 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
209 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800210 const ConnectFailedCallback& onConnectFailed,
211 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800212{
213 if (error ||
214 remoteEndpoint == ip::tcp::resolver::iterator())
215 {
216 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
217 return;
218
219 socket->close();
220 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800221
222 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
223 << error.category().message(error.value()));
224
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800225 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
226 error.category().message(error.value()));
227 return;
228 }
229
230 // got endpoint, now trying to connect (only try the first resolution option)
231 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800232 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
233 socket, timer,
234 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800235}
236
237
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800238} // namespace nfd