/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** * Copyright (C) 2014 Named Data Networking Project * See COPYING for copyright and distribution information. */ #include "tcp-channel.hpp" #include "core/global-io.hpp" #include "core/face-uri.hpp" namespace nfd { NFD_LOG_INIT("TcpChannel"); using namespace boost::asio; TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint) : m_localEndpoint(localEndpoint) { this->setUri(FaceUri(localEndpoint)); } TcpChannel::~TcpChannel() { } void TcpChannel::listen(const FaceCreatedCallback& onFaceCreated, const ConnectFailedCallback& onAcceptFailed, int backlog/* = tcp::acceptor::max_connections*/) { m_acceptor = make_shared(boost::ref(getGlobalIoService())); m_acceptor->open(m_localEndpoint.protocol()); m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true)); if (m_localEndpoint.address().is_v6()) { m_acceptor->set_option(ip::v6_only(true)); } m_acceptor->bind(m_localEndpoint); m_acceptor->listen(backlog); shared_ptr clientSocket = make_shared(boost::ref(getGlobalIoService())); m_acceptor->async_accept(*clientSocket, bind(&TcpChannel::handleSuccessfulAccept, this, _1, clientSocket, onFaceCreated, onAcceptFailed)); } void TcpChannel::connect(const tcp::Endpoint& remoteEndpoint, const TcpChannel::FaceCreatedCallback& onFaceCreated, const TcpChannel::ConnectFailedCallback& onConnectFailed, const time::Duration& timeout/* = time::seconds(4)*/) { ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint); if (i != m_channelFaces.end()) { onFaceCreated(i->second); return; } shared_ptr clientSocket = make_shared(boost::ref(getGlobalIoService())); shared_ptr connectTimeoutTimer = make_shared(boost::ref(getGlobalIoService())); clientSocket->async_connect(remoteEndpoint, bind(&TcpChannel::handleSuccessfulConnect, this, _1, clientSocket, connectTimeoutTimer, onFaceCreated, onConnectFailed)); connectTimeoutTimer->expires_from_now(timeout); connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1, clientSocket, connectTimeoutTimer, onConnectFailed)); } void TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort, const TcpChannel::FaceCreatedCallback& onFaceCreated, const TcpChannel::ConnectFailedCallback& onConnectFailed, const time::Duration& timeout/* = time::seconds(4)*/) { shared_ptr clientSocket = make_shared(boost::ref(getGlobalIoService())); shared_ptr connectTimeoutTimer = make_shared(boost::ref(getGlobalIoService())); ip::tcp::resolver::query query(remoteHost, remotePort); shared_ptr resolver = make_shared(boost::ref(getGlobalIoService())); resolver->async_resolve(query, bind(&TcpChannel::handleEndpointResolution, this, _1, _2, clientSocket, connectTimeoutTimer, onFaceCreated, onConnectFailed, resolver)); connectTimeoutTimer->expires_from_now(timeout); connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1, clientSocket, connectTimeoutTimer, onConnectFailed)); } size_t TcpChannel::size() const { return m_channelFaces.size(); } void TcpChannel::createFace(const shared_ptr& socket, const FaceCreatedCallback& onFaceCreated) { tcp::Endpoint remoteEndpoint = socket->remote_endpoint(); shared_ptr face; if (socket->local_endpoint().address().is_loopback()) face = make_shared(boost::cref(socket)); else face = make_shared(boost::cref(socket)); face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint); onFaceCreated(face); m_channelFaces[remoteEndpoint] = face; } void TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint) { NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint); m_channelFaces.erase(remoteEndpoint); } void TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error, const shared_ptr& socket, const FaceCreatedCallback& onFaceCreated, const ConnectFailedCallback& onAcceptFailed) { if (error) { if (error == boost::system::errc::operation_canceled) // when socket is closed by someone return; NFD_LOG_DEBUG("Connect to remote endpoint failed: " << error.category().message(error.value())); if (static_cast(onAcceptFailed)) onAcceptFailed("Connect to remote endpoint failed: " + error.category().message(error.value())); return; } // prepare accepting the next connection shared_ptr clientSocket = make_shared(boost::ref(getGlobalIoService())); m_acceptor->async_accept(*clientSocket, bind(&TcpChannel::handleSuccessfulAccept, this, _1, clientSocket, onFaceCreated, onAcceptFailed)); NFD_LOG_DEBUG("[" << m_localEndpoint << "] " "<< Connection from " << socket->remote_endpoint()); createFace(socket, onFaceCreated); } void TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error, const shared_ptr& socket, const shared_ptr& timer, const FaceCreatedCallback& onFaceCreated, const ConnectFailedCallback& onConnectFailed) { timer->cancel(); if (error) { if (error == boost::system::errc::operation_canceled) // when socket is closed by someone return; socket->close(); NFD_LOG_DEBUG("Connect to remote endpoint failed: " << error.category().message(error.value())); onConnectFailed("Connect to remote endpoint failed: " + error.category().message(error.value())); return; } NFD_LOG_DEBUG("[" << m_localEndpoint << "] " ">> Connection to " << socket->remote_endpoint()); createFace(socket, onFaceCreated); } void TcpChannel::handleFailedConnect(const boost::system::error_code& error, const shared_ptr& socket, const shared_ptr& timer, const ConnectFailedCallback& onConnectFailed) { if (error) { // e.g., cancelled return; } NFD_LOG_DEBUG("Connect to remote endpoint timed out: " << error.category().message(error.value())); onConnectFailed("Connect to remote endpoint timed out: " + error.category().message(error.value())); socket->close(); // abort the connection } void TcpChannel::handleEndpointResolution(const boost::system::error_code& error, ip::tcp::resolver::iterator remoteEndpoint, const shared_ptr& socket, const shared_ptr& timer, const FaceCreatedCallback& onFaceCreated, const ConnectFailedCallback& onConnectFailed, const shared_ptr& resolver) { if (error || remoteEndpoint == ip::tcp::resolver::iterator()) { if (error == boost::system::errc::operation_canceled) // when socket is closed by someone return; socket->close(); timer->cancel(); NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: " << error.category().message(error.value())); onConnectFailed("Remote endpoint hostname or port cannot be resolved: " + error.category().message(error.value())); return; } // got endpoint, now trying to connect (only try the first resolution option) socket->async_connect(*remoteEndpoint, bind(&TcpChannel::handleSuccessfulConnect, this, _1, socket, timer, onFaceCreated, onConnectFailed)); } } // namespace nfd