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
master
madmaxoft@gmail.com 2012-02-17 13:09:56 +00:00
parent 71097aa49b
commit 62c4ed1c2e
2 changed files with 288 additions and 208 deletions

View File

@ -35,9 +35,9 @@
#include <iostream> #include <iostream>
#ifndef _WIN32 #ifndef _WIN32
#include <cstring> #include <cstring>
#include <sys/time.h> #include <sys/time.h>
#define SD_SEND 0x01 #define SD_SEND 0x01
#else #else
#define MSG_NOSIGNAL (0) #define MSG_NOSIGNAL (0)
#endif #endif
@ -50,80 +50,135 @@ using namespace std;
int Socket::nofSockets_= 0; int Socket::nofSockets_= 0;
void Socket::Start() {
#ifdef _WIN32
if (!nofSockets_) {
WSADATA info;
if (WSAStartup(MAKEWORD(2,0), &info)) { void Socket::Start()
throw "Could not start WSA"; {
} #ifdef _WIN32
} if (!nofSockets_)
#endif {
++nofSockets_; WSADATA info;
if (WSAStartup(MAKEWORD(2,0), &info))
{
throw "Could not start WSA";
}
}
#endif
++nofSockets_;
} }
void Socket::End() {
void Socket::End()
{
#ifdef _WIN32 #ifdef _WIN32
WSACleanup(); WSACleanup();
#endif #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 */ ) void Socket::Close( bool a_WaitSend /* = false */ )
{ {
if( s_ ) if (IsValid())
{ {
if( a_WaitSend ) if (a_WaitSend)
{ {
assert( shutdown(s_, SD_SEND ) == 0 ); assert( shutdown(s_, SD_SEND ) == 0 );
char c; 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_); 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 "";
} std::string Socket::ReceiveLine()
ret += r; {
if (r == '\n') return ret; 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; std::string ret;
while( ret.size() < a_Length ) { while( ret.size() < a_Length )
{
char r; char r;
if (recv(s_, &r, 1, 0) <= 0 ) if (recv(s_, &r, 1, 0) <= 0 )
@ -164,138 +241,127 @@ std::string Socket::ReceiveBytes( unsigned int a_Length ) {
return ret; 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" ); void Socket::SendLine(std::string s)
Close(); {
} 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) { SocketServer::SocketServer(int port, int connections, TypeSocket type)
return; {
} sockaddr_in sa;
#ifdef _WIN32 memset(&sa, 0, sizeof(sa));
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 */ sa.sin_family = PF_INET;
if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) { sa.sin_port = htons(port);
closesocket(s_); s_ = socket(AF_INET, SOCK_STREAM, 0);
LOG("WebServer: INVALID_SOCKET");
}
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* SocketServer::Accept()
{ {
Socket * r = new Socket(accept(s_, 0, 0));
if (!r->IsValid())
{
delete r;
r = NULL;
}
SOCKET new_sock = accept(s_, 0, 0); return r;
#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;
} }
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 SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type)
error = strerror(WSAGetLastError()); {
#endif FD_ZERO(&fds_);
throw error; SOCKET Highest = s1->s_;
} FD_SET(const_cast<Socket*>(s1)->s_,&fds_);
if(s2)
{
FD_SET(const_cast<Socket*>(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<Socket*>(s1)->s_,&fds_);
if(s2) {
FD_SET(const_cast<Socket*>(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;
}

View File

@ -39,6 +39,7 @@
#ifndef _WIN32 #ifndef _WIN32
typedef int SOCKET; typedef int SOCKET;
#define SOCKET_ERROR (-1) #define SOCKET_ERROR (-1)
#define INVALID_SOCKET (-1)
#define closesocket close #define closesocket close
#endif // !_WIN32 #endif // !_WIN32
@ -48,66 +49,79 @@
enum TypeSocket {BlockingSocket, NonBlockingSocket}; enum TypeSocket {BlockingSocket, NonBlockingSocket};
class Socket {
class Socket
{
public: public:
virtual ~Socket(); virtual ~Socket();
Socket(const Socket&); Socket(const Socket&);
Socket& operator=(Socket&); Socket& operator=(Socket&);
std::string ReceiveLine(); std::string ReceiveLine();
std::string ReceiveBytes( unsigned int a_Length ); 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 // The parameter of SendLine is not a const reference
// because SendLine modifes the std::string passed. // because SendLine modifes the std::string passed.
void SendLine (std::string); void SendLine (std::string);
// The parameter of SendBytes is a const reference // The parameter of SendBytes is a const reference
// because SendBytes does not modify the std::string passed // because SendBytes does not modify the std::string passed
// (in contrast to SendLine). // (in contrast to SendLine).
void SendBytes(const std::string&); void SendBytes(const std::string&);
protected: protected:
friend class SocketServer; friend class SocketServer;
friend class SocketSelect; friend class SocketSelect;
Socket(SOCKET s); Socket(SOCKET s);
Socket(); Socket();
SOCKET s_; SOCKET s_;
int* refCounter_; int* refCounter_;
private: private:
static void Start(); static void Start();
static void End(); static void End();
static int nofSockets_; static int nofSockets_;
}; };
class SocketClient : public Socket {
class SocketServer :
public Socket
{
public: 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 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/wsapiref_2tiq.asp
class SocketSelect { class SocketSelect
public: {
SocketSelect(Socket const * const s1, Socket const * const s2=NULL, TypeSocket type=BlockingSocket); 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: private:
fd_set fds_; fd_set fds_;
}; };
#endif #endif