From d8ac99a0374b528caca66ad8ecb5fb36f6334e77 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 14:58:52 +0100 Subject: [PATCH] cNetwork: Implemented connection shutdown and close. --- src/OSSupport/Network.cpp | 68 ++++++++++++++++++++++++++++-------- src/OSSupport/Network.h | 10 +++--- tests/Network/EchoServer.cpp | 16 ++++++++- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index a34f7ebca..2ef6ace3c 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -87,6 +87,9 @@ public: a_Address and a_AddrLen describe the remote peer that has connected. */ cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); + /** Destroys the LibEvent handle representing the link. */ + ~cTCPLinkImpl(); + /** Queues a connection request to the specified host. a_ConnectCallbacks must be valid. The object must have been constructed by the right constructor (without the Socket param). */ @@ -98,8 +101,8 @@ public: virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } + virtual void Shutdown(void) override; virtual void Close(void) override; - virtual void Drop(void) override; protected: @@ -164,6 +167,9 @@ public: cTCPLink::cCallbacksPtr a_LinkCallbacks ); + /** Closes the server, dropping all the connections. */ + ~cServerHandleImpl(); + /** Starts listening on the specified port. Both IPv4 and IPv6 interfaces are used, if possible. */ bool Listen(UInt16 a_Port); @@ -499,6 +505,15 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L +cTCPLinkImpl::~cTCPLinkImpl() +{ + bufferevent_free(m_BufferEvent); +} + + + + + /** Schedules the actual connection request. Returns true on success, false on failure. */ bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) @@ -556,20 +571,31 @@ bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) -void cTCPLinkImpl::Close(void) +void cTCPLinkImpl::Shutdown(void) { - // TODO - ASSERT(!"cTCPLinkImpl::Close(): Not implemented yet"); + #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::Drop(void) +void cTCPLinkImpl::Close(void) { - // TODO - ASSERT(!"cTCPLinkImpl::Drop(): Not implemented yet"); + bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); + if (m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(this); + } + else + { + m_Server->RemoveLink(this); + } } @@ -736,24 +762,38 @@ cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallb -void cServerHandleImpl::Close(void) +cServerHandleImpl::~cServerHandleImpl() { - // Stop the listener sockets: - evconnlistener_free(m_ConnListener); - m_ConnListener = nullptr; + if (m_ConnListener != nullptr) + { + evconnlistener_free(m_ConnListener); + } if (m_SecondaryConnListener != nullptr) { evconnlistener_free(m_SecondaryConnListener); - m_SecondaryConnListener = nullptr; + } +} + + + + + +void cServerHandleImpl::Close(void) +{ + // Stop the listener sockets: + evconnlistener_disable(m_ConnListener); + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_disable(m_SecondaryConnListener); } m_IsListening = false; - // Close all connections: + // Shutdown all connections: cTCPLinkImplPtrs Conns; std::swap(Conns, m_Connections); for (auto conn: Conns) { - conn->Close(); + conn->Shutdown(); } } diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 3f60b03a7..11c45b152 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -66,12 +66,13 @@ public: virtual UInt16 GetRemotePort(void) const = 0; /** Closes the link gracefully. - The link will keep trying to send the queued data, then it will send the FIN packet. */ - virtual void Close(void) = 0; + The link will send any queued outgoing data, then it will send the FIN packet. + The link will still receive incoming data from remote until the remote closes the connection. */ + virtual void Shutdown(void) = 0; /** Drops the connection without any more processing. Sends the RST packet, queued outgoing and incoming data is lost. */ - virtual void Drop(void) = 0; + virtual void Close(void) = 0; protected: /** Callbacks to be used for the various situations. */ @@ -95,7 +96,8 @@ public: // Force a virtual destructor for all descendants: virtual ~cServerHandle() {} - /** Stops the server, no more incoming connections will be accepted. */ + /** Stops the server, no more incoming connections will be accepted. + All current connections will be shut down (cTCPLink::Shutdown()). */ virtual void Close(void) = 0; /** Returns true if the server has been started correctly and is currently listening for incoming connections. */ diff --git a/tests/Network/EchoServer.cpp b/tests/Network/EchoServer.cpp index 3d9a6228d..1e0ac023f 100644 --- a/tests/Network/EchoServer.cpp +++ b/tests/Network/EchoServer.cpp @@ -38,6 +38,16 @@ class cEchoLinkCallbacks: LOGD("%p (%s:%d): Data received (%u bytes), echoing back.", &a_Link, a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort(), static_cast(a_Size)); a_Link.Send(a_Data, a_Size); LOGD("Echo queued"); + + // Search for a Ctrl+Z, if found, drop the connection: + for (size_t i = 0; i < a_Size; i++) + { + if (a_Data[i] == '\x1a') + { + a_Link.Close(); + return; + } + } } virtual void OnRemoteClosed(cTCPLink & a_Link) override @@ -67,7 +77,7 @@ int main() ASSERT(Server->IsListening()); // Wait for the user to terminate the server: - printf("Press enter to terminate the server.\n"); + printf("Press enter to close the server.\n"); AString line; std::getline(std::cin, line); @@ -75,6 +85,10 @@ int main() LOG("Server terminating."); Server->Close(); ASSERT(!Server->IsListening()); + LOGD("Server has been closed."); + + printf("Press enter to exit test.\n"); + std::getline(std::cin, line); LOG("Network test finished."); return 0;