This change allows it to work correctly on WSL, since WSL must be bound to ADDR_ANY. The overall behavior remains unchanged.
545 lines
11 KiB
C++
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);
|
|
}
|