From c0cb787c101725a649d26de68fca2632c82830ba Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 11:57:16 +0100 Subject: cNetwork: Split the main cpp file into several files. --- src/OSSupport/TCPLinkImpl.cpp | 314 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 src/OSSupport/TCPLinkImpl.cpp (limited to 'src/OSSupport/TCPLinkImpl.cpp') diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp new file mode 100644 index 000000000..bcacc0569 --- /dev/null +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -0,0 +1,314 @@ + +// TCPLinkImpl.cpp + +// Implements the cTCPLinkImpl class implementing the TCP link functionality + +#include "Globals.h" +#include "TCPLinkImpl.h" +#include "NetworkSingleton.h" +#include "ServerHandleImpl.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLinkImpl: + +cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), + m_Server(nullptr) +{ + // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), + m_Server(a_Server) +{ + // Update the endpoint addresses: + UpdateLocalAddress(); + UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); + + // Create the LibEvent handle: + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +cTCPLinkImpl::~cTCPLinkImpl() +{ + bufferevent_free(m_BufferEvent); +} + + + + + +cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) +{ + ASSERT(a_LinkCallbacks != nullptr); + ASSERT(a_ConnectCallbacks != nullptr); + + // Create a new link: + cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible + res->m_ConnectCallbacks = a_ConnectCallbacks; + cNetworkSingleton::Get().AddLink(res); + + // If a_Host is an IP address, schedule a connection immediately: + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) == 0) + { + // Insert the correct port: + if (sa.ss_family == AF_INET6) + { + reinterpret_cast(&sa)->sin6_port = htons(a_Port); + } + else + { + reinterpret_cast(&sa)->sin_port = htons(a_Port); + } + + // Queue the connect request: + if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast(&sa), salen) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; + } + + // a_Host is a hostname, connect after a lookup: + if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; +} + + + + + +bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) +{ + return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); +} + + + + + +void cTCPLinkImpl::Shutdown(void) +{ + #ifdef _WIN32 + shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); + #else + shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); + #endif + bufferevent_disable(m_BufferEvent, EV_WRITE); +} + + + + + +void cTCPLinkImpl::Close(void) +{ + // Disable all events on the socket, but keep it alive: + bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); + if (m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(this); + } + else + { + m_Server->RemoveLink(this); + } +} + + + + + + +void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + ASSERT(Self->m_Callbacks != nullptr); + + // Read all the incoming data, in 1024-byte chunks: + char data[1024]; + size_t length; + while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) + { + Self->m_Callbacks->OnReceivedData(*Self, data, length); + } +} + + + + + +void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + + // If an error is reported, call the error callback: + if (a_What & BEV_EVENT_ERROR) + { + // Choose the proper callback to call based on whether we were waiting for connection or not: + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR()); + } + else + { + Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); + if (Self->m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(Self); + } + else + { + Self->m_Server->RemoveLink(Self); + } + } + return; + } + + // Pending connection succeeded, call the connection callback: + if (a_What & BEV_EVENT_CONNECTED) + { + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnSuccess(*Self); + // Reset the connect callbacks so that later errors get reported through the link callbacks: + Self->m_ConnectCallbacks.reset(); + return; + } + Self->UpdateLocalAddress(); + Self->UpdateRemoteAddress(); + } + + // If the connection has been closed, call the link callback and remove the connection: + if (a_What & BEV_EVENT_EOF) + { + Self->m_Callbacks->OnRemoteClosed(*Self); + if (Self->m_Server != nullptr) + { + Self->m_Server->RemoveLink(Self); + } + else + { + cNetworkSingleton::Get().RemoveLink(Self); + } + return; + } + + // Unknown event, report it: + LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What); + ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event"); +} + + + + + +void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) +{ + // Based on the family specified in the address, use the correct datastructure to convert to IP string: + char IP[128]; + switch (a_Address->sa_family) + { + case AF_INET: // IPv4: + { + const sockaddr_in * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: // IPv6 + { + const sockaddr_in6 * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin6_port); + break; + } + + default: + { + LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family); + ASSERT(!"Unknown socket address family"); + break; + } + } + a_IP.assign(IP); +} + + + + + +void cTCPLinkImpl::UpdateLocalAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast(sizeof(sa)); + getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); +} + + + + + +void cTCPLinkImpl::UpdateRemoteAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast(sizeof(sa)); + getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_RemoteIP, m_RemotePort); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::Connect( + const AString & a_Host, + const UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + // Add a connection request to the queue: + cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); + return (Conn != nullptr); +} + + + + + -- cgit v1.2.3