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>
#ifndef _WIN32
#include <cstring>
#include <sys/time.h>
#define SD_SEND 0x01
#include <cstring>
#include <sys/time.h>
#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<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
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