From 62c4ed1c2e81615f54ade6b4e3b1d77798468105 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Fri, 17 Feb 2012 13:09:56 +0000 Subject: [PATCH] Fixed *nix hang on server stop (socket needs shutting down) git-svn-id: http://mc-server.googlecode.com/svn/trunk@283 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- WebServer/Socket.cpp | 410 +++++++++++++++++++++++++------------------ WebServer/Socket.h | 86 +++++---- 2 files changed, 288 insertions(+), 208 deletions(-) diff --git a/WebServer/Socket.cpp b/WebServer/Socket.cpp index 1c48172e..6a5f51ee 100644 --- a/WebServer/Socket.cpp +++ b/WebServer/Socket.cpp @@ -35,9 +35,9 @@ #include #ifndef _WIN32 -#include -#include -#define SD_SEND 0x01 + #include + #include + #define SD_SEND 0x01 #else #define MSG_NOSIGNAL (0) #endif @@ -50,80 +50,135 @@ using namespace std; int Socket::nofSockets_= 0; -void Socket::Start() { -#ifdef _WIN32 - if (!nofSockets_) { - WSADATA info; - if (WSAStartup(MAKEWORD(2,0), &info)) { - throw "Could not start WSA"; - } - } -#endif - ++nofSockets_; + + + + +void Socket::Start() +{ + #ifdef _WIN32 + if (!nofSockets_) + { + WSADATA info; + if (WSAStartup(MAKEWORD(2,0), &info)) + { + throw "Could not start WSA"; + } + } + #endif + ++nofSockets_; } -void Socket::End() { + + + + +void Socket::End() +{ #ifdef _WIN32 - WSACleanup(); + WSACleanup(); #endif } -Socket::Socket() : s_(0) { - Start(); - // UDP: use SOCK_DGRAM instead of SOCK_STREAM - s_ = socket(AF_INET,SOCK_STREAM,0); -#ifdef _WIN32 - if(s_ ==INVALID_SOCKET) -#else - if(s_ < 0) -#endif - { - throw "INVALID_SOCKET"; - } - refCounter_ = new int(1); + + +Socket::Socket() : + s_(INVALID_SOCKET) +{ + Start(); + // UDP: use SOCK_DGRAM instead of SOCK_STREAM + s_ = socket(AF_INET,SOCK_STREAM,0); + + if (!IsValid()) + { + throw "INVALID_SOCKET"; + } + + refCounter_ = new int(1); } -Socket::Socket(SOCKET s) : s_(s) { - Start(); - refCounter_ = new int(1); + + + + +Socket::Socket(SOCKET s) : s_(s) +{ + Start(); + refCounter_ = new int(1); }; -Socket::~Socket() { - if (! --(*refCounter_)) { - Close(); - delete refCounter_; - } - --nofSockets_; - if (!nofSockets_) End(); + + + +Socket::~Socket() +{ + if (! --(*refCounter_)) + { + Close(); + delete refCounter_; + } + + --nofSockets_; + if (!nofSockets_) + { + End(); + } } -Socket::Socket(const Socket& o) { - refCounter_=o.refCounter_; - (*refCounter_)++; - s_ =o.s_; - nofSockets_++; + + + +Socket::Socket(const Socket& o) +{ + refCounter_=o.refCounter_; + (*refCounter_)++; + s_ =o.s_; + + nofSockets_++; } -Socket& Socket::operator=(Socket& o) { - (*o.refCounter_)++; - refCounter_=o.refCounter_; - s_ =o.s_; - nofSockets_++; - return *this; + +Socket& Socket::operator=(Socket& o) +{ + (*o.refCounter_)++; + + refCounter_ = o.refCounter_; + s_ = o.s_; + + nofSockets_++; + + return *this; } + + + + +bool Socket::IsValid(void) const +{ + #ifdef _WIN32 + return (s_ != INVALID_SOCKET); + #else + return (s_ >= 0); + #endif +} + + + + + void Socket::Close( bool a_WaitSend /* = false */ ) { - if( s_ ) + if (IsValid()) { - if( a_WaitSend ) + if (a_WaitSend) { assert( shutdown(s_, SD_SEND ) == 0 ); char c; @@ -131,28 +186,50 @@ void Socket::Close( bool a_WaitSend /* = false */ ) {} } + #ifndef _WIN32 + // Linux needs to shut the socket down first, otherwise the socket keeps blocking accept() and select() calls! + // Ref.: http://forum.mc-server.org/showthread.php?tid=344 + if (shutdown(s_, SHUT_RDWR) != 0) + { + LOGWARN("Error on shutting down socket %d", s_); + } + #endif // _WIN32 + closesocket(s_); - s_ = 0; + + s_ = INVALID_SOCKET; } } -std::string Socket::ReceiveLine() { - std::string ret; - while (1) { - char r; - if (recv(s_, &r, 1, 0) <= 0 ) - { - return ""; - } - ret += r; - if (r == '\n') return ret; - } + + + +std::string Socket::ReceiveLine() +{ + std::string ret; + while (1) + { + char r; + + if (recv(s_, &r, 1, 0) <= 0 ) + { + return ""; + } + ret += r; + if (r == '\n') return ret; + } } -std::string Socket::ReceiveBytes( unsigned int a_Length ) { + + + + +std::string Socket::ReceiveBytes( unsigned int a_Length ) +{ std::string ret; - while( ret.size() < a_Length ) { + while( ret.size() < a_Length ) + { char r; if (recv(s_, &r, 1, 0) <= 0 ) @@ -164,138 +241,127 @@ std::string Socket::ReceiveBytes( unsigned int a_Length ) { return ret; } -void Socket::SendLine(std::string s) { - s += '\n'; - if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 ) - { - //printf("SendLine Socket error!! \n" ); - Close(); - } + + + + +void Socket::SendLine(std::string s) +{ + s += '\n'; + if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 ) + { + //printf("SendLine Socket error!! \n" ); + Close(); + } } -void Socket::SendBytes(const std::string& s) { - if( send(s_,s.c_str(),s.length(), MSG_NOSIGNAL) <= 0 ) - { - //printf("SendBytes Socket error!! \n" ); - Close(); - } + + + + +void Socket::SendBytes(const std::string& s) +{ + if( send(s_,s.c_str(),s.length(), MSG_NOSIGNAL) <= 0 ) + { + //printf("SendBytes Socket error!! \n" ); + Close(); + } } -SocketServer::SocketServer(int port, int connections, TypeSocket type) { - sockaddr_in sa; - memset(&sa, 0, sizeof(sa)); - sa.sin_family = PF_INET; - sa.sin_port = htons(port); - s_ = socket(AF_INET, SOCK_STREAM, 0); -#ifdef _WIN32 - if(s_ ==INVALID_SOCKET) -#else - if(s_ < 0) -#endif - { - LOG("WebServer: INVALID_SOCKET"); - } - if(type==NonBlockingSocket) { - return; - } +SocketServer::SocketServer(int port, int connections, TypeSocket type) +{ + sockaddr_in sa; -#ifdef _WIN32 - char yes = 1; -#else - int yes = 1; -#endif - if (setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) { - LOG("WebServer: setsockopt == -1"); - return; - } + memset(&sa, 0, sizeof(sa)); - /* bind the socket to the internet address */ - if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) { - closesocket(s_); - LOG("WebServer: INVALID_SOCKET"); - } + sa.sin_family = PF_INET; + sa.sin_port = htons(port); + s_ = socket(AF_INET, SOCK_STREAM, 0); - listen(s_, connections); + if (!IsValid()) + { + LOG("WebServer: INVALID_SOCKET"); + } + + if(type==NonBlockingSocket) + { + return; + } + + #ifdef _WIN32 + char yes = 1; + #else + int yes = 1; + #endif + + if (setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) + { + LOG("WebServer: setsockopt == -1"); + return; + } + + /* bind the socket to the internet address */ + if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) + { + closesocket(s_); + LOG("WebServer: INVALID_SOCKET"); + } + + listen(s_, connections); } + + + + Socket* SocketServer::Accept() { + Socket * r = new Socket(accept(s_, 0, 0)); + if (!r->IsValid()) + { + delete r; + r = NULL; + } - SOCKET new_sock = accept(s_, 0, 0); -#ifdef _WIN32 - if(new_sock==INVALID_SOCKET || s_ == 0) -#else - if(new_sock < 0 || s_ == 0) -#endif - { - #ifdef _WIN32 - int rc = WSAGetLastError(); - if(rc==WSAEWOULDBLOCK) { - return 0; // non-blocking call, no request pending - } - else - #endif - { - //throw "Invalid Socket"; - return 0; - } - } - - Socket* r = new Socket(new_sock); - return r; + return r; } -SocketClient::SocketClient(const std::string& host, int port) : Socket() { - std::string error; - hostent *he; - if ((he = gethostbyname(host.c_str())) == 0) { -#ifdef _WIN32 - error = strerror(errno); -#endif - throw error; - } - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr = *((in_addr *)he->h_addr); - memset(&(addr.sin_zero), 0, 8); - if (::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) { -#ifdef _WIN32 - error = strerror(WSAGetLastError()); -#endif - throw error; - } + +SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) +{ + FD_ZERO(&fds_); + SOCKET Highest = s1->s_; + FD_SET(const_cast(s1)->s_,&fds_); + if(s2) + { + FD_SET(const_cast(s2)->s_,&fds_); + if (s2->s_ > Highest) + { + Highest = s2->s_; + } + } + if (select(Highest + 1, &fds_, NULL, NULL, NULL) == SOCKET_ERROR) + { + throw "Error in select"; + } } -#ifndef _WIN32 -struct timeval; -#endif -SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) { - FD_ZERO(&fds_); - FD_SET(const_cast(s1)->s_,&fds_); - if(s2) { - FD_SET(const_cast(s2)->s_,&fds_); - } -#ifdef _WIN32 - TIMEVAL *ptval = 0; -#else - timeval *ptval = 0; -#endif - if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR) - throw "Error in select"; + +bool SocketSelect::Readable(Socket const* const s) +{ + return (FD_ISSET(s->s_,&fds_)); } -bool SocketSelect::Readable(Socket const* const s) { - if (FD_ISSET(s->s_,&fds_)) return true; - return false; -} + + + diff --git a/WebServer/Socket.h b/WebServer/Socket.h index e38df69e..5f124810 100644 --- a/WebServer/Socket.h +++ b/WebServer/Socket.h @@ -39,6 +39,7 @@ #ifndef _WIN32 typedef int SOCKET; #define SOCKET_ERROR (-1) + #define INVALID_SOCKET (-1) #define closesocket close #endif // !_WIN32 @@ -48,66 +49,79 @@ enum TypeSocket {BlockingSocket, NonBlockingSocket}; -class Socket { + + + + +class Socket +{ public: - virtual ~Socket(); - Socket(const Socket&); - Socket& operator=(Socket&); + virtual ~Socket(); + Socket(const Socket&); + Socket& operator=(Socket&); - std::string ReceiveLine(); - std::string ReceiveBytes( unsigned int a_Length ); + std::string ReceiveLine(); + std::string ReceiveBytes( unsigned int a_Length ); + + bool IsValid(void) const; - void Close( bool a_WaitSend = false ); + void Close( bool a_WaitSend = false ); - // The parameter of SendLine is not a const reference - // because SendLine modifes the std::string passed. - void SendLine (std::string); + // The parameter of SendLine is not a const reference + // because SendLine modifes the std::string passed. + void SendLine (std::string); - // The parameter of SendBytes is a const reference - // because SendBytes does not modify the std::string passed - // (in contrast to SendLine). - void SendBytes(const std::string&); + // The parameter of SendBytes is a const reference + // because SendBytes does not modify the std::string passed + // (in contrast to SendLine). + void SendBytes(const std::string&); protected: - friend class SocketServer; - friend class SocketSelect; + friend class SocketServer; + friend class SocketSelect; - Socket(SOCKET s); - Socket(); + Socket(SOCKET s); + Socket(); - SOCKET s_; + SOCKET s_; - int* refCounter_; + int* refCounter_; private: - static void Start(); - static void End(); - static int nofSockets_; + static void Start(); + static void End(); + static int nofSockets_; }; -class SocketClient : public Socket { + + + + +class SocketServer : + public Socket +{ public: - SocketClient(const std::string& host, int port); + SocketServer(int port, int connections, TypeSocket type=BlockingSocket); + + Socket* Accept(); }; -class SocketServer : public Socket { -public: - SocketServer(int port, int connections, TypeSocket type=BlockingSocket); - Socket* Accept(); -}; + + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/wsapiref_2tiq.asp -class SocketSelect { - public: - SocketSelect(Socket const * const s1, Socket const * const s2=NULL, TypeSocket type=BlockingSocket); +class SocketSelect +{ +public: + SocketSelect(Socket const * const s1, Socket const * const s2=NULL, TypeSocket type=BlockingSocket); - bool Readable(Socket const * const s); + bool Readable(Socket const * const s); - private: - fd_set fds_; +private: + fd_set fds_; }; #endif