mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
232 lines
5.2 KiB
C++
232 lines
5.2 KiB
C++
#include "global_define.h"
|
|
#include "tcp_server.h"
|
|
#include "../common/eqemu_logsys.h"
|
|
|
|
#include <stdio.h>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#ifdef _WINDOWS
|
|
#include <process.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#define INVALID_SOCKET -1
|
|
#define SOCKET_ERROR -1
|
|
#endif
|
|
|
|
#define SERVER_LOOP_GRANULARITY 3 //# of ms between checking our socket/queues
|
|
|
|
BaseTCPServer::BaseTCPServer(uint16 in_port) {
|
|
NextID = 1;
|
|
pPort = in_port;
|
|
sock = 0;
|
|
pRunLoop = true;
|
|
#ifdef _WINDOWS
|
|
_beginthread(BaseTCPServer::TCPServerLoop, 0, this);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, nullptr, &BaseTCPServer::TCPServerLoop, this);
|
|
#endif
|
|
}
|
|
|
|
BaseTCPServer::~BaseTCPServer() {
|
|
StopLoopAndWait();
|
|
}
|
|
|
|
void BaseTCPServer::StopLoopAndWait() {
|
|
MRunLoop.lock();
|
|
if(pRunLoop) {
|
|
pRunLoop = false;
|
|
MRunLoop.unlock();
|
|
//wait for loop to stop.
|
|
MLoopRunning.lock();
|
|
MLoopRunning.unlock();
|
|
} else {
|
|
MRunLoop.unlock();
|
|
}
|
|
}
|
|
|
|
bool BaseTCPServer::RunLoop() {
|
|
bool ret;
|
|
MRunLoop.lock();
|
|
ret = pRunLoop;
|
|
MRunLoop.unlock();
|
|
return ret;
|
|
}
|
|
|
|
ThreadReturnType BaseTCPServer::TCPServerLoop(void* tmp) {
|
|
#ifdef _WINDOWS
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
#endif
|
|
if (tmp == 0) {
|
|
THREAD_RETURN(nullptr);
|
|
}
|
|
BaseTCPServer* tcps = (BaseTCPServer*) tmp;
|
|
|
|
#ifndef WIN32
|
|
LogDebug( "Starting TCPServerLoop with thread ID [{}]", pthread_self());
|
|
#endif
|
|
|
|
tcps->MLoopRunning.lock();
|
|
while (tcps->RunLoop()) {
|
|
Sleep(SERVER_LOOP_GRANULARITY);
|
|
tcps->Process();
|
|
}
|
|
tcps->MLoopRunning.unlock();
|
|
|
|
#ifndef WIN32
|
|
LogDebug( "Ending TCPServerLoop with thread ID [{}]", pthread_self());
|
|
#endif
|
|
|
|
THREAD_RETURN(nullptr);
|
|
}
|
|
|
|
void BaseTCPServer::Process() {
|
|
ListenNewConnections();
|
|
}
|
|
|
|
void BaseTCPServer::ListenNewConnections() {
|
|
SOCKET tmpsock;
|
|
struct sockaddr_in from;
|
|
struct in_addr in;
|
|
unsigned int fromlen;
|
|
unsigned short port;
|
|
|
|
from.sin_family = AF_INET;
|
|
fromlen = sizeof(from);
|
|
LockMutex lock(&MSock);
|
|
#ifndef DARWIN // Corysia - On OSX, 0 is a valid fd.
|
|
if (!sock)
|
|
return;
|
|
#else
|
|
if (sock == -1) return;
|
|
#endif
|
|
|
|
// Check for pending connects
|
|
#ifdef _WINDOWS
|
|
unsigned long nonblocking = 1;
|
|
while ((tmpsock = accept(sock, (struct sockaddr*) &from, (int *) &fromlen)) != INVALID_SOCKET) {
|
|
ioctlsocket (tmpsock, FIONBIO, &nonblocking);
|
|
#else
|
|
#ifdef __CYGWIN__
|
|
while ((tmpsock = accept(sock, (struct sockaddr *) &from, (int *) &fromlen)) != INVALID_SOCKET) {
|
|
#else
|
|
while ((tmpsock = accept(sock, (struct sockaddr*) &from, &fromlen)) != INVALID_SOCKET) {
|
|
#endif
|
|
fcntl(tmpsock, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
|
|
setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize));
|
|
port = from.sin_port;
|
|
in.s_addr = from.sin_addr.s_addr;
|
|
|
|
// New TCP connection, this must consume the socket.
|
|
CreateNewConnection(GetNextID(), tmpsock, in.s_addr, ntohs(from.sin_port));
|
|
}
|
|
}
|
|
|
|
bool BaseTCPServer::Open(uint16 in_port, char* errbuf) {
|
|
if (errbuf)
|
|
errbuf[0] = 0;
|
|
LockMutex lock(&MSock);
|
|
if (sock != 0) {
|
|
if (errbuf)
|
|
snprintf(errbuf, TCPServer_ErrorBufferSize, "Listening socket already open");
|
|
return false;
|
|
}
|
|
if (in_port != 0) {
|
|
pPort = in_port;
|
|
}
|
|
|
|
#ifdef _WINDOWS
|
|
SOCKADDR_IN address;
|
|
unsigned long nonblocking = 1;
|
|
#else
|
|
struct sockaddr_in address;
|
|
#endif
|
|
int reuse_addr = 1;
|
|
|
|
// Setup internet address information.
|
|
// This is used with the bind() call
|
|
memset((char *) &address, 0, sizeof(address));
|
|
address.sin_family = AF_INET;
|
|
address.sin_port = htons(pPort);
|
|
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
// Setting up TCP port for new TCP connections
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock == INVALID_SOCKET) {
|
|
if (errbuf)
|
|
snprintf(errbuf, TCPServer_ErrorBufferSize, "socket(): INVALID_SOCKET");
|
|
return false;
|
|
}
|
|
|
|
// Quag: dont think following is good stuff for TCP, good for UDP
|
|
// Mis: SO_REUSEADDR shouldn't be a problem for tcp--allows you to restart
|
|
// without waiting for conns in TIME_WAIT to die
|
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr));
|
|
|
|
|
|
if (bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) {
|
|
#ifdef _WINDOWS
|
|
closesocket(sock);
|
|
#else
|
|
close(sock);
|
|
#endif
|
|
sock = 0;
|
|
if (errbuf)
|
|
sprintf(errbuf, "bind(): <0");
|
|
return false;
|
|
}
|
|
|
|
int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
|
|
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize));
|
|
#ifdef _WINDOWS
|
|
ioctlsocket (sock, FIONBIO, &nonblocking);
|
|
#else
|
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
|
|
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
|
|
#ifdef _WINDOWS
|
|
closesocket(sock);
|
|
if (errbuf)
|
|
snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %d", WSAGetLastError());
|
|
#else
|
|
close(sock);
|
|
if (errbuf)
|
|
snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno));
|
|
#endif
|
|
sock = 0;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BaseTCPServer::Close() {
|
|
StopLoopAndWait();
|
|
|
|
LockMutex lock(&MSock);
|
|
if (sock) {
|
|
#ifdef _WINDOWS
|
|
closesocket(sock);
|
|
#else
|
|
close(sock);
|
|
#endif
|
|
}
|
|
sock = 0;
|
|
}
|
|
|
|
bool BaseTCPServer::IsOpen() {
|
|
MSock.lock();
|
|
bool ret = (bool) (sock != 0);
|
|
MSock.unlock();
|
|
return ret;
|
|
}
|
|
|