Files
m2dev-server-src/src/libthecore/socket.cpp
ErLullo 23ba2d7cdf Allow WSL to work correctly by binding to ADDR_ANY
This change allows it to work correctly on WSL, since WSL must be bound to ADDR_ANY. The overall behavior remains unchanged.
2026-02-18 17:44:08 +01:00

545 lines
11 KiB
C++

#include "stdafx.h"
#ifndef OS_WINDOWS
#include <netinet/tcp.h>
#endif
/* Forwards */
void socket_lingeron(socket_t s);
void socket_lingeroff(socket_t s);
void socket_timeout(socket_t s, long sec, long usec);
void socket_reuse(socket_t s);
void socket_keepalive(socket_t s);
int socket_read(socket_t desc, char* read_point, size_t space_left)
{
int ret;
ret = recv(desc, read_point, space_left, 0);
if (ret > 0)
return ret;
if (ret == 0) // 정상적으로 접속 끊김
return -1;
#ifdef EINTR /* Interrupted system call - various platforms */
if (errno == EINTR)
return (0);
#endif
#ifdef EAGAIN /* POSIX */
if (errno == EAGAIN)
return (0);
#endif
#ifdef EWOULDBLOCK /* BSD */
if (errno == EWOULDBLOCK)
return (0);
#endif /* EWOULDBLOCK */
#ifdef EDEADLK /* Macintosh */
if (errno == EDEADLK)
return (0);
#endif
#ifdef OS_WINDOWS
int wsa_error = WSAGetLastError();
if (wsa_error == WSAEWOULDBLOCK) {
return 0;
}
sys_log(0, "socket_read: WSAGetLastError returned %d", wsa_error);
#endif
sys_err("about to lose connection");
return -1;
}
int socket_write_tcp(socket_t desc, const char *txt, int length)
{
int bytes_written = send(desc, txt, length, 0);
// 성공
if (bytes_written > 0)
return (bytes_written);
if (bytes_written == 0)
return -1;
#ifdef EAGAIN /* POSIX */
if (errno == EAGAIN)
return 0;
#endif
#ifdef EWOULDBLOCK /* BSD */
if (errno == EWOULDBLOCK)
return 0;
#endif
#ifdef EDEADLK /* Macintosh */
if (errno == EDEADLK)
return 0;
#endif
#ifdef OS_WINDOWS
int wsa_error = WSAGetLastError();
if (wsa_error == WSAEWOULDBLOCK) {
return 0;
}
sys_log(0, "socket_write_tcp: WSAGetLastError returned %d", wsa_error);
#endif
/* Looks like the error was fatal. Too bad. */
return -1;
}
int socket_write(socket_t desc, const char *data, size_t length)
{
size_t total;
int bytes_written;
total = length;
do
{
if ((bytes_written = socket_write_tcp(desc, data, total)) < 0)
{
#ifdef EWOULDBLOCK
if (errno == EWOULDBLOCK)
errno = EAGAIN;
#endif
if (errno == EAGAIN)
sys_err("socket write would block, about to close!");
else
sys_err("write to desc error"); // '보통' 상대편으로 부터 접속이 끊긴 것이다.
return -1;
}
else
{
data += bytes_written;
total -= bytes_written;
}
}
while (total > 0);
return 0;
}
int socket_bind(const char * ip, int port, int protocol)
{
int s;
#ifdef __WIN32
SOCKADDR_IN sa;
#else
struct sockaddr_in sa;
#endif
if ((s = socket(AF_INET, protocol, 0)) < 0)
{
sys_err("socket: %s", strerror(errno));
return -1;
}
socket_reuse(s);
#ifndef OS_WINDOWS
socket_lingeroff(s);
#else
// Winsock2: SO_DONTLINGER, SO_KEEPALIVE, SO_LINGER, and SO_OOBINLINE are
// not supported on sockets of type SOCK_DGRAM
if (protocol == SOCK_STREAM) {
socket_lingeroff(s);
}
#endif
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
//윈도우 서버는 개발용으로만 쓰기 때문에 BIND ip를 INADDR_ANY로 고정
//(테스트의 편의성을 위해)
#ifdef OS_FREEBSD
sa.sin_addr.s_addr = inet_addr(ip);
#else
sa.sin_addr.s_addr = INADDR_ANY;
#endif
sa.sin_port = htons((unsigned short) port);
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
{
sys_err("bind: %s", strerror(errno));
return -1;
}
socket_nonblock(s);
if (protocol == SOCK_STREAM)
{
sys_log(0, "SYSTEM: BINDING TCP PORT ON [%d] (fd %d)", port, s);
listen(s, SOMAXCONN);
}
else
sys_log(0, "SYSTEM: BINDING UDP PORT ON [%d] (fd %d)", port, s);
return s;
}
int socket_tcp_bind(const char * ip, int port)
{
return socket_bind(ip, port, SOCK_STREAM);
}
void socket_close(socket_t s)
{
#ifdef OS_WINDOWS
closesocket(s);
#else
close(s);
#endif
}
socket_t socket_accept(socket_t s, struct sockaddr_in *peer)
{
socket_t desc;
socklen_t i;
i = sizeof(*peer);
if ((desc = accept(s, (struct sockaddr *) peer, &i)) == -1)
{
sys_err("accept: %s (fd %d)", strerror(errno), s);
return -1;
}
if (desc >= 65500)
{
sys_err("SOCKET FD 65500 LIMIT! %d", desc);
socket_close(s);
return -1;
}
socket_nonblock(desc);
socket_lingeroff(desc);
socket_nodelay(desc);
socket_keepalive(desc);
return (desc);
}
socket_t socket_connect(const char* host, WORD port)
{
socket_t s = 0;
struct sockaddr_in server_addr;
int rslt;
/* 소켓주소 구조체 초기화 */
memset(&server_addr, 0, sizeof(server_addr));
if (isdigit(*host))
server_addr.sin_addr.s_addr = inet_addr(host);
else
{
struct hostent *hp;
if ((hp = gethostbyname(host)) == NULL)
{
sys_err("socket_connect(): can not connect to %s:%d", host, port);
return -1;
}
thecore_memcpy((char* ) &server_addr.sin_addr, hp->h_addr, sizeof(server_addr.sin_addr));
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
socket_keepalive(s);
socket_sndbuf(s, 524288); // Increased from 233KB to 512KB
socket_rcvbuf(s, 524288); // Increased from 233KB to 512KB
socket_timeout(s, 10, 0);
socket_lingeron(s);
socket_nodelay(s);
/* 연결요청 */
if ((rslt = connect(s, (struct sockaddr *) &server_addr, sizeof(server_addr))) < 0)
{
socket_close(s);
#ifdef OS_WINDOWS
switch (WSAGetLastError())
#else
switch (rslt)
#endif
{
#ifdef OS_WINDOWS
case WSAETIMEDOUT:
#else
case EINTR:
#endif
sys_err("HOST %s:%d connection timeout.", host, port);
break;
#ifdef OS_WINDOWS
case WSAECONNREFUSED:
#else
case ECONNREFUSED:
#endif
sys_err("HOST %s:%d port is not opened. connection refused.", host, port);
break;
#ifdef OS_WINDOWS
case WSAENETUNREACH:
#else
case ENETUNREACH:
#endif
sys_err("HOST %s:%d is not reachable from this host.", host, port);
break;
default:
sys_err("HOST %s:%d, could not connect.", host, port);
break;
}
perror("connect");
return (-1);
}
return (s);
}
#ifndef OS_WINDOWS
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
void socket_nonblock(socket_t s)
{
int flags;
flags = fcntl(s, F_GETFL, 0);
flags |= O_NONBLOCK;
if (fcntl(s, F_SETFL, flags) < 0)
{
sys_err("fcntl: nonblock: %s", strerror(errno));
return;
}
}
void socket_block(socket_t s)
{
int flags;
flags = fcntl(s, F_GETFL, 0);
flags &= ~O_NONBLOCK;
if (fcntl(s, F_SETFL, flags) < 0)
{
sys_err("fcntl: nonblock: %s", strerror(errno));
return;
}
}
#else
void socket_nonblock(socket_t s)
{
unsigned long val = 1;
ioctlsocket(s, FIONBIO, &val);
}
void socket_block(socket_t s)
{
unsigned long val = 0;
ioctlsocket(s, FIONBIO, &val);
}
#endif
void socket_dontroute(socket_t s)
{
int set;
if (setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (const char *) &set, sizeof(int)) < 0)
{
sys_err("setsockopt: dontroute: %s", strerror(errno));
socket_close(s);
return;
}
}
void socket_lingeroff(socket_t s)
{
#ifdef OS_WINDOWS
int linger;
linger = 0;
#else
struct linger linger;
linger.l_onoff = 0;
linger.l_linger = 0;
#endif
if (setsockopt(s, SOL_SOCKET, SO_LINGER, (const char*) &linger, sizeof(linger)) < 0)
{
sys_err("setsockopt: linger: %s", strerror(errno));
socket_close(s);
return;
}
}
void socket_lingeron(socket_t s)
{
#ifdef OS_WINDOWS
int linger;
linger = 0;
#else
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 0;
#endif
if (setsockopt(s, SOL_SOCKET, SO_LINGER, (const char*) &linger, sizeof(linger)) < 0)
{
sys_err("setsockopt: linger: %s", strerror(errno));
socket_close(s);
return;
}
}
void socket_rcvbuf(socket_t s, unsigned int opt)
{
socklen_t optlen;
optlen = sizeof(opt);
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*) &opt, optlen) < 0)
{
sys_err("setsockopt: rcvbuf: %s", strerror(errno));
socket_close(s);
return;
}
opt = 0;
optlen = sizeof(opt);
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &opt, &optlen) < 0)
{
sys_err("getsockopt: rcvbuf: %s", strerror(errno));
socket_close(s);
return;
}
sys_log(1, "SYSTEM: %d: receive buffer changed to %d", s, opt);
}
void socket_sndbuf(socket_t s, unsigned int opt)
{
socklen_t optlen;
optlen = sizeof(opt);
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*) &opt, optlen) < 0)
{
sys_err("setsockopt: sndbuf: %s", strerror(errno));
return;
}
opt = 0;
optlen = sizeof(opt);
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*) &opt, &optlen) < 0)
{
sys_err("getsockopt: sndbuf: %s", strerror(errno));
return;
}
sys_log(1, "SYSTEM: %d: send buffer changed to %d", s, opt);
}
// sec : seconds, usec : microseconds
void socket_timeout(socket_t s, long sec, long usec)
{
#ifndef OS_WINDOWS
struct timeval rcvopt;
struct timeval sndopt;
socklen_t optlen = sizeof(rcvopt);
rcvopt.tv_sec = sndopt.tv_sec = sec;
rcvopt.tv_usec = sndopt.tv_usec = usec;
#else
socklen_t rcvopt, sndopt;
socklen_t optlen = sizeof(rcvopt);
sndopt = rcvopt = (sec * 1000) + (usec / 1000);
#endif
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*) &rcvopt, optlen) < 0)
{
sys_err("setsockopt: timeout: %s", strerror(errno));
socket_close(s);
return;
}
if (getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &rcvopt, &optlen) < 0)
{
sys_err("getsockopt: timeout: %s", strerror(errno));
socket_close(s);
return;
}
optlen = sizeof(sndopt);
if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*) &sndopt, optlen) < 0)
{
sys_err("setsockopt: timeout: %s", strerror(errno));
socket_close(s);
return;
}
if (getsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &sndopt, &optlen) < 0)
{
sys_err("getsockopt: timeout: %s", strerror(errno));
socket_close(s);
return;
}
#ifndef OS_WINDOWS
sys_log(1, "SYSTEM: %d: TIMEOUT RCV: %d.%d, SND: %d.%d", s, rcvopt.tv_sec, rcvopt.tv_usec, sndopt.tv_sec, sndopt.tv_usec);
#endif
}
void socket_reuse(socket_t s)
{
int opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) &opt, sizeof(opt)) < 0)
{
sys_err("setsockopt: reuse: %s", strerror(errno));
socket_close(s);
return;
}
}
void socket_keepalive(socket_t s)
{
int opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (const char*) &opt, sizeof(opt)) < 0)
{
perror("setsockopt: keepalive");
socket_close(s);
return;
}
}
void socket_nodelay(socket_t s)
{
int opt = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt, sizeof(opt)) < 0)
{
sys_err("setsockopt: TCP_NODELAY: %s", strerror(errno));
return;
}
sys_log(1, "SYSTEM: %d: TCP_NODELAY enabled", s);
}