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-c427514a06d6master
parent
71097aa49b
commit
62c4ed1c2e
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue