【C】一个跨平台TCPIP协议通讯的例子,网络包裹转发代理,非阻塞IO,单线程,可实用
原文链接:https://www.0xaa55.com/thread-1991-1-1.html转载请保留出处。
我运营着一个正版Minecraft服务器,我本人在国外,而服务器在国内,服务器虽然是双线的,但我无论是通过电信还是联通的线路去连接它,延迟都很大,电信线路基本上260ms到1200ms不等,有时候能到4000ms甚至12000ms(在点进“多人游戏”界面后,看到的数值就是这么大)。而联通线路则是平时都500ms以上,偶尔电信gg的时候联通可以降到300ms左右,虽然“据说”联通比较容易连接国外,但我这边的效果就是,联通并不能联通……
关键是,我的数据发到服务器很快,而服务器的数据发到我这却延迟很大,通过在游戏界面打字说话可以看出,我在某个时间段发的话,在对应时间段插入到整体聊天记录的时候,我并不能看到我打的字,但其他玩家能看到。也就是上传快,响应慢。
我买了一个韩国VPN,当我设置了Win7的VPN连接以后,总体延迟稳定在了260ms到350ms左右,偶尔还是比单独连接差一些,但至少不会突然给我掉到1000ms左右。
不过这个并不是一个好的解决办法,用了VPN后,我机器上所有的网络出口就都是这个VPN了,包括我的虚拟机里的一堆东西,也是只能走VPN出去,但我只想让minecraft走VPN出去。。
正好手上有个闲置的亚马逊AWS,免费试用的那种,或许我可以让它连上VPN后,我再让它帮我把minecraft的网络包裹通过VPN转发到国内,估计速度会不错。
于是我先统计了一下各个peer之间响应的情况,结果如下:
我ping minecraft服务器的延迟是280ms左右,但下午6点之后就会到1000ms以上,并且保持到晚上22点……。
我ping我的亚马逊AWS的服,7ms,毫无波动。
我ping我的VPN,延迟50ms左右,偶尔上升到70ms,高峰期会到150ms。
我的亚马逊AWS服ping我的VPN,延迟40ms左右,偶尔到45ms。
我的亚马逊AWS服ping minecraft服务器,延迟平均270ms,如果持续一段时间没有ping它,然后再ping的话,延迟会增加到350ms
我的亚马逊AWS服连接我的VPN后ping minecraft服务器,延迟平均162ms,偶尔上下波动3ms左右。
于是我写了个C语言的TCPIP包裹转发器,让它在我的亚马逊AWS上运行,然后我让我的亚马逊AWS连接VPN,并且设置出口到我的minecraft服务器的所有地址都走VPN,这样我的程序就可以把包裹转发进VPN了。
这其中为了测试它的效果,我把它写成了能在Windows运行的版本,只不过在Windows里面它并不是一个Daemon,也不是一个Service,而是一个控制台程序,不断地用printf来打印连接与断开的情况。
顺带我已经把这玩意儿搞到github上了:https://github.com/0xAA55/tcpfwd
这里面有很多坑,是需要Windows开发者编写跨平台的Linux程序所需要注意的。
1、头文件的有无的问题,这个请看https://www.0xaa55.com/thread-1997-1-1.html
2、Linux如何写Daemon守护进程(也就是服务),这个不难,但输出信息到Log文件对于调试不是十分直观。建议调试的时候,不写成daemon,而是直接把内容输出到stdout,然后用screen把它单独弄成一个窗口,可随时观看。
·因为你要是写了个呆萌,然后用less +F看log的话,你的呆萌挂掉了你都不知道,详见第五条。
3、Linux的socket套接字是一个文件描述符,Windows的不是。
4、Linux的socket编程,错误码看errno,Windows看WSAGetLastError(),注意返回的数值不同,Windows的有个WSAE开头。Windows的socket编程,错误码不会被写入到errno。
5、有一个可能让Windows开发者崩溃的神坑:Linux下如果你send或recv的那个TCPIP套接字的连接被断开了,你会收到一个SIGPIPE信号,也就是“管道断开”,你要是不处理它,它默认给你exit(0),直接杀进程。
6、用非阻塞IO套接字,轮询(poll)所有的套接字接受数据的时候,如果你想释放CPU使用率,注意Windows的Sleep()按毫秒算,Linux的sleep()按秒算。其次是Linux写sleep(0)没啥问题,Windows写Sleep(0)会在Win98系统里永久地睡下去。。。因此Windows里写Sleep(1)比较安全。然后,你要是用了#define sleep Sleep,并且参数给的是(1)的话,到时候延迟上千不是梦。。。
源码:tcpfwd.c//#include"config.h" // tired to configure
// These we could have
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<stdarg.h>
#include<errno.h>
#include<ctype.h>
#include<signal.h>
#include<string.h>
#include<time.h>
// There's something we have and there's something we didn't have
#ifdef WIN32
#include<WinSock2.h>
#include<ws2tcpip.h> // for using struct sockaddr_in6 ... but now it didn't support IPv6
#include<xmmintrin.h>
#else
#include<fcntl.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#endif
#define EXEC_NAME "tcpfwd" // program name
#define LOCK_FILE "/tmp/tcpfwd.lock" // PID file name
#define LOG_FILE "tcpfwd.log" // log file name
#define CFG_FILE "tcpfwd.conf" // configure file name
#define backlog_default 200 // default max connections
#define num_conn_alloc 32 // how much units allocated when RAM usage grows
#define fwd_buffer_size 8192 // forward buffer size
#define def_packet_size 1472 // mtu for splitting packets
#define MAX_BACKOFF_ITERS (RAND_MAX > 0x10000 ? 0x10000 : RAND_MAX)
#define MAX_BO_SLEEPS 20
typedef int bool_t, *bool_p;
// my address structure
typedef union address_u
{
struct sockaddr sa;
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
struct sockaddr_storage sa_stor;
}address_t, *address_p;
#ifdef WIN32
#define cpu_relax() _mm_pause()
typedef INT_PTR ssize_t; // signed size_t in windows
typedef int socklen_t;
static void sleep_relax(unsigned sleeps)
{
Sleep(sleeps); // In windows, Sleep(0) will die in win98
}
static int _socket_errno() // Translate error number
{
int Err = WSAGetLastError();
switch(Err)
{
case WSAEADDRINUSE:
return EADDRINUSE;
case WSAEADDRNOTAVAIL:
return EADDRNOTAVAIL;
case WSAEAFNOSUPPORT:
return EAFNOSUPPORT;
case WSAEALREADY:
return EALREADY;
case WSAECONNABORTED:
return ECONNABORTED;
case WSAECONNREFUSED:
return ECONNREFUSED;
case WSAECONNRESET:
return ECONNRESET;
case WSAEDESTADDRREQ:
return EDESTADDRREQ;
case WSAEHOSTUNREACH:
return EHOSTUNREACH;
case WSAEINPROGRESS:
return EINPROGRESS;
case WSAEISCONN:
return EISCONN;
case WSAELOOP:
return ELOOP;
case WSAEMSGSIZE:
return EMSGSIZE;
case WSAENETDOWN:
return ENETDOWN;
case WSAENETRESET:
return ENETRESET;
case WSAENETUNREACH:
return ENETUNREACH;
case WSAENOBUFS:
return ENOBUFS;
case WSAENOPROTOOPT:
return ENOPROTOOPT;
case WSAENOTCONN:
return ENOTCONN;
case WSAENOTSOCK:
return ENOTSOCK;
case WSAEOPNOTSUPP:
return EOPNOTSUPP;
case WSAEPROTONOSUPPORT:
return EPROTONOSUPPORT;
case WSAEPROTOTYPE:
return EPROTOTYPE;
case WSAETIMEDOUT:
return ETIMEDOUT;
case WSAEWOULDBLOCK:
return EWOULDBLOCK;
default:
return Err;
}
}
#define socket_errno _socket_errno() // pretend as a variable
#define MSG_NOSIGNAL 0 // Windows didn't have this, pretend as we have
#else // we don't think about what other systems, just for unix
#if __x86_64__ || i386
#define cpu_relax() __asm__ __volatile__("pause")
#elif __arm__ || __aarch64__
#define cpu_relax() __asm__ __volatile__("yield")
#elif __mips__
#define cpu_relax() __asm__ __volatile__(".word 0x00000140")
#else
#define cpu_relax() __asm__ __volatile__("pause")
#endif
static void sleep_relax(unsigned sleeps)
{
if(sleeps)
usleep(sleeps * 1000);
else
usleep(500);
}
#define socket_errno errno
static int closesocket(int sockfd) // sockets in linux is a file descriptor
{
return close(sockfd);
}
#define _tzset tzset
static char*_strtime(char*timestr)
{
time_t now;
struct tm *l_time;
static char _timestr = {0};
now = time(NULL);
l_time = localtime(&now);
sprintf(_timestr, "%.2d:%.2d:%.2d", l_time->tm_hour, l_time->tm_min, l_time->tm_sec);
strncpy(timestr, _timestr, sizeof _timestr);
return _timestr;
}
#endif
//=============================================================================
// socket connection
typedef struct fwdconn_struct
{
int sockfd_src; // source
int sockfd_dst; // destination
bool_t connected; // was that connected?
char buffer_src; // data from source
int cb_src; // data bytes
char buffer_dst; // data from destination
int cb_dst; // data bytes
address_t addr_from; // source address
socklen_t addr_size; // source address length
}fwdconn_t, *fwdconn_p;
typedef struct fwdroute_struct
{
int listen_socket; // a socket for accepting connections
int import; // listening port
int export; // output port
unsigned packet_size_to_src; // mtu split size for source
unsigned packet_size_to_dst; // mtu split size for destination
int status_of_tcp_nodelay; // use nagle algorithm? 1 for no, 0 for yes
address_t DestAddr; // destination address
socklen_t cbDestAddr; // destination address length
fwdconn_p fwd_conn; // active connection array
size_t num_fwd_conn; // how many
size_t max_fwd_conn; // array capacity
}fwdroute_t, *fwdroute_p;
typedef struct fwdinst_struct
{
fwdroute_p pRoute; // routers
size_t num_route; // how many
size_t max_route; // array capacity
int listen_backlog; // max connections
bool_t log_traffic; // do we log any packets?
}fwdinst_t, *fwdinst_p;
typedef struct bo_struct
{
unsigned cr;
unsigned sr;
unsigned max_cr;
unsigned max_sr;
}bo_t, *bo_p;
static void bo_reset(bo_p bo)
{
memset(bo, 0, sizeof *bo);
bo->max_cr = MAX_BACKOFF_ITERS;
bo->max_sr = MAX_BO_SLEEPS;
}
static void bo_update(bo_p bo)
{
int s = 0;
if(bo->cr < bo->max_cr)
{
if(!bo->cr) bo->cr = 1;
else bo->cr <<= 1;
}
else
{
bo->cr = bo->max_cr;
s = 1;
}
if(s)
{
if(bo->sr < bo->max_sr)
{
if(!bo->sr) bo->sr = 1;
else bo->sr <<= 1;
}
else bo->sr = bo->max_sr;
sleep_relax(rand() % bo->sr);
}
else
{
int r = rand() % bo->cr + 1;
while(r--) cpu_relax();
}
}
static volatile bool_t _g_Term = 0; // global quitting?
static bool_t _g_LogToScreen = 1; // do we write log to screen
void Inst_Log(char *message, ...);
fwdinst_p Inst_Create(bool_t DoDaemonize, const char *cfg_file);
int Inst_Run(fwdinst_p pInst);
void Inst_Term(fwdinst_p pInst);
static int _Inst_Daemonize();
static bool_t _SetSocketBlockingEnabled(int fd, int blocking);
static int _CreateNBIOTCPSocket();
static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port);
static bool_t _ListenPort(fwdinst_p pInst, int socket, int port);
static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
static fwdroute_p _Inst_AddRoute
(
fwdinst_p pInst,
int import,
uint32_t exportAddr,
int exportPort,
int status_of_tcp_nodelay,
int packet_size_to_src,
int packet_size_to_dst
);
static fwdconn_p _Inst_AddNewConnection
(
fwdroute_p pRoute,
int sockfd,
address_p pAddr,
socklen_t cbAddr
);
static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic);
static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4);
static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
static void _signal_handler(int sig);
//==============================================================================
//Func: _SetSocketBlockingEnabled
//Desc: make a socket works for polling
//------------------------------------------------------------------------------
static bool_t _SetSocketBlockingEnabled(int fd, int blocking)
{
if (fd < 0)
return 0;
#ifdef WIN32
{
unsigned long mode = blocking ? 0 : 1;
if(ioctlsocket(fd, FIONBIO, &mode) == 0)
return 1;
else
{
Inst_Log("Set socket to non blocking I/O mode failed. %d\n",
socket_errno);
return 0;
}
}
#else
int flags = fcntl(fd, F_GETFL, 0);
if(flags < 0)return 0;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
if(!fcntl(fd, F_SETFL, flags))
return 1;
else
{
Inst_Log("Set socket to non blocking I/O mode failed.\n");
return 0;
}
#endif
}
//==============================================================================
//Func: _signal_handler
//Desc: signal handler
//------------------------------------------------------------------------------
static void _signal_handler(int sig)
{
switch(sig)
{
#ifndef WIN32
case SIGHUP:
break;
#endif
case SIGTERM:
Inst_Log("Terminate signal.\n");
_g_Term = 1;
break;
}
}
void Inst_LogTime()
{
char szTime = {0};
_strtime(szTime);
Inst_Log("[%s]: ", szTime);
}
//==============================================================================
//Func: Inst_Log
//Desc: Print log to a specified file.
//------------------------------------------------------------------------------
void Inst_Log(char *format, ...)
{
FILE *logfile = NULL;
va_list ap;
logfile=fopen(LOG_FILE,"a");
if(logfile)
{
va_start(ap, format);
vfprintf(logfile, format, ap);
va_end(ap);
fclose(logfile);
}
if(_g_LogToScreen)
{
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
}
}
//==============================================================================
//Func: Inst_LogAddrV4
//Desc: Print an IPv4 address to log
//------------------------------------------------------------------------------
void Inst_LogAddrV4(const address_p pAddr)
{
char strIp;
#ifdef WIN32
sprintf(strIp, "%u.%u.%u.%u",
pAddr->sa_in.sin_addr.S_un.S_un_b.s_b1,
pAddr->sa_in.sin_addr.S_un.S_un_b.s_b2,
pAddr->sa_in.sin_addr.S_un.S_un_b.s_b3,
pAddr->sa_in.sin_addr.S_un.S_un_b.s_b4);
#else
inet_ntop(AF_INET, &pAddr->sa_in.sin_addr,
strIp, INET_ADDRSTRLEN);
#endif
Inst_Log("%s:%u", strIp, htons(pAddr->sa_in.sin_port));
}
//==============================================================================
//Func: Inst_Create
//Desc: Create a new instance class
//------------------------------------------------------------------------------
fwdinst_p Inst_Create(bool_t DoDaemonize, const char*cfg_file)
{
fwdinst_p pInst = NULL;
_tzset();
if(DoDaemonize && !_Inst_Daemonize())
return NULL;
pInst = malloc(sizeof(fwdinst_t));
if(!pInst)
{
Inst_LogTime();
Inst_Log("Out of memory.\n");
return NULL;
}
memset(pInst, 0, sizeof(fwdinst_t));
Inst_LogTime();
Inst_Log("Starting "EXEC_NAME"\n");
if(!_Inst_LoadCFG(pInst, cfg_file))
goto ErrHandler;
return pInst;
ErrHandler:
Inst_Term(pInst);
return NULL;
}
//==============================================================================
//Func: _CreateNBIOTCPSocket
//Desc: Create a new socket, and set it to non blocking I/O mode
//------------------------------------------------------------------------------
static int _CreateNBIOTCPSocket()
{
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd == -1)
{
Inst_LogTime();
Inst_Log("Create socket failed. %d\n", socket_errno);
return-1;
}
if(!_SetSocketBlockingEnabled(fd, 0))
{
closesocket(fd);
return -1;
}
return fd;
}
//==============================================================================
//Func: _CreateNBIOTCPSocketWithConnection
//Desc: Create a new socket with connection, and set it to non blocking I/O mode
//------------------------------------------------------------------------------
static int _CreateNBIOTCPSocketWithConnection
(
const address_p addr,
socklen_t addrlen
)
{
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd == -1)
{
Inst_LogTime();
Inst_Log("Create socket failed.\n");
return-1;
}
if(!_SetSocketBlockingEnabled(fd, 0))
{
closesocket(fd);
return -1;
}
Inst_LogTime();
Inst_Log("Connecting ");
Inst_LogAddrV4(addr);
Inst_Log(" ...\n");
if(connect(fd, &addr->sa, addrlen) < 0)
{
int Err = socket_errno;
switch(Err)
{
case EINPROGRESS:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
default:
Inst_LogTime();
Inst_Log("Connect ");
Inst_LogAddrV4(addr);
Inst_Log(" failed: %d.\n", Err);
closesocket(fd);
return -1;
}
}
return fd;
}
//==============================================================================
//Func: _MakeIPv4Address
//Desc: Make an IPv4 address by using given IP:Port
//------------------------------------------------------------------------------
static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port)
{
memset(pOut, 0, sizeof(pOut->sa_in));
pOut->sa_in.sin_family = AF_INET;
pOut->sa_in.sin_port = htons(Port);
pOut->sa_in.sin_addr.s_addr = htonl(IP);
}
//==============================================================================
//Func: _ListenPort
//Desc: Let a socket listen to a specified port.
//------------------------------------------------------------------------------
static bool_t _ListenPort(fwdinst_p pInst, int sockfd, int port)
{
address_t Addr;
int reuse_addr = 1;
_MakeIPv4Address(&Addr, 0, port);
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr)) < 0)
{
Inst_LogTime();
Inst_Log("Warning: set SO_REUSEADDR failed. %d\n", socket_errno);
return 0;
}
if(bind(sockfd, &Addr.sa, sizeof(Addr.sa)) < 0)
{
Inst_LogTime();
Inst_Log("Bind port %d failed. %d\n", port, socket_errno);
return 0;
}
if(listen(sockfd, pInst->listen_backlog) < 0)
{
int Err = socket_errno;
switch(Err)
{
case EADDRINUSE:
Inst_LogTime();
Inst_Log("Listen to port failed: port already in use.\n");
return 0;
default:
Inst_LogTime();
Inst_Log("Listen to port failed: %d\n", Err);
return 0;
}
}
Inst_LogTime();
Inst_Log("Listening to port %d.\n", port);
return 1;
}
//==============================================================================
//Func: _Inst_AddRoute
//Desc: Add a route rule for the instance
//------------------------------------------------------------------------------
static fwdroute_p _Inst_AddRoute
(
fwdinst_p pInst,
int import,
uint32_t exportAddr,
int exportPort,
int status_of_tcp_nodelay, // Use nagle algorithm? 1=no, 0=yes
int packet_size_to_src,
int packet_size_to_dst
)
{
size_t cur;
if(pInst->num_route >= pInst->max_route)
{
fwdroute_p pnew = realloc(pInst->pRoute,
(pInst->max_route + 16) * sizeof(fwdroute_t));
if(!pnew)
{
Inst_LogTime();
Inst_Log("Out of memory.\n");
return NULL;
}
pInst->pRoute = pnew;
pInst->max_route += 16;
}
cur = pInst->num_route;
memset(&pInst->pRoute, 0, sizeof(fwdroute_t));
if(packet_size_to_src <= 0)
packet_size_to_src = def_packet_size;
if(packet_size_to_dst <= 0)
packet_size_to_dst = def_packet_size;
//Initialize
pInst->pRoute.import = import;
pInst->pRoute.export = exportPort;
pInst->pRoute.packet_size_to_src = packet_size_to_src;
pInst->pRoute.packet_size_to_dst = packet_size_to_dst;
_MakeIPv4Address(&pInst->pRoute.DestAddr, exportAddr, exportPort);
pInst->pRoute.cbDestAddr = sizeof(struct sockaddr);
pInst->pRoute.status_of_tcp_nodelay = status_of_tcp_nodelay;
Inst_LogTime();
Inst_Log("Redirect: localhost:%d -> ", import);
Inst_LogAddrV4(&pInst->pRoute.DestAddr);
Inst_Log(", TCP_NODELAY = %d, packet_size_to_src = %d, packet_size_to_dst = %d\n",
pInst->pRoute.status_of_tcp_nodelay,
pInst->pRoute.packet_size_to_src,
pInst->pRoute.packet_size_to_dst);
if(pInst->pRoute.packet_size_to_src > 8192)
{
Inst_LogTime();
Inst_Log("Warning: `packet to source' should not exceed 8192 bytes.\n");
pInst->pRoute.packet_size_to_src = 8192;
}
if(pInst->pRoute.packet_size_to_dst > 8192)
{
Inst_LogTime();
Inst_Log("Warning: `packet to dest' size should not exceed 8192 bytes.\n");
pInst->pRoute.packet_size_to_dst = 8192;
}
//Create the socket
pInst->pRoute.listen_socket = _CreateNBIOTCPSocket();
if(pInst->pRoute.listen_socket == -1)
return NULL;
if(!_ListenPort(pInst, pInst->pRoute.listen_socket, import))
return NULL;
pInst->num_route++;
return &pInst->pRoute;
}
//==============================================================================
//Func: _Inst_AddNewConnection
//Desc: Add a socket to a route, called when accepted a new connection.
//------------------------------------------------------------------------------
static fwdconn_p _Inst_AddNewConnection
(
fwdroute_p pRoute,
int sockfd,
address_p pAddr,
socklen_t cbAddr
)
{
int sockfd_out = _CreateNBIOTCPSocketWithConnection(&pRoute->DestAddr,
pRoute->cbDestAddr);
if(sockfd_out == -1)
return NULL;
if(setsockopt(sockfd_out, IPPROTO_TCP, TCP_NODELAY,
(char*)&pRoute->status_of_tcp_nodelay,
sizeof(pRoute->status_of_tcp_nodelay)) < 0)
{
Inst_LogTime();
Inst_Log("Warning: setsockopt(TCP_NODELAY) failed:%d.\n", socket_errno);
}
if(pRoute->num_fwd_conn >= pRoute->max_fwd_conn)
{
fwdconn_p pnew = realloc(pRoute->fwd_conn,
(pRoute->max_fwd_conn + num_conn_alloc) * sizeof(fwdconn_t));
if(!pnew)
{
Inst_LogTime();
Inst_Log("Out of memory.\n");
return 0;
}
pRoute->fwd_conn = pnew;
pRoute->max_fwd_conn += num_conn_alloc;
}
memset(&pRoute->fwd_conn, 0, sizeof(fwdconn_t));
pRoute->fwd_conn.sockfd_src = sockfd;
pRoute->fwd_conn.sockfd_dst = sockfd_out;
pRoute->fwd_conn.addr_from = *pAddr;
pRoute->fwd_conn.addr_size = cbAddr;
return &pRoute->fwd_conn;
}
//==============================================================================
//Func: _Inst_BreakConnection
//Desc: close a specific connection.
//------------------------------------------------------------------------------
static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic)
{
if(ic >= pRoute->num_fwd_conn)
return;
Inst_LogTime();
Inst_Log("Connection closed: from ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log(" to ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
if(pRoute->fwd_conn.sockfd_src != -1)
closesocket(pRoute->fwd_conn.sockfd_src);
pRoute->fwd_conn.sockfd_src = -1;
if(pRoute->fwd_conn.sockfd_dst != -1)
closesocket(pRoute->fwd_conn.sockfd_dst);
pRoute->fwd_conn.sockfd_dst = -1;
if(pRoute->num_fwd_conn > 1)
{
pRoute->fwd_conn = pRoute->fwd_conn[--pRoute->num_fwd_conn];
if(pRoute->num_fwd_conn + num_conn_alloc < pRoute->max_fwd_conn)
{
pRoute->max_fwd_conn -= num_conn_alloc;
if(pRoute->max_fwd_conn)
{
fwdconn_p pshrink = realloc(pRoute->fwd_conn,
pRoute->max_fwd_conn * sizeof(fwdconn_t));
if(pshrink)
pRoute->fwd_conn = pshrink;
}
}
}
else
pRoute->num_fwd_conn = 0;
}
//==============================================================================
//Func: _IpAddrV4ByNums
//Desc: Combine 4 numbers to an ipv4 address
//------------------------------------------------------------------------------
static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4)
{
return
((n4 & 0xFF)) |
((n3 & 0xFF) << 8) |
((n2 & 0xFF) << 16) |
((n1 & 0xFF) << 24);
}
//==============================================================================
//Func: _Inst_LoadCFG
//Desc: Load configurations
//------------------------------------------------------------------------------
static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file)
{
FILE*cfgfile = NULL;
char LineBuf = {0};
char*pChr;
unsigned LineNo = 0;
cfgfile = fopen(cfg_file, "r");
if(!cfgfile)
{
Inst_LogTime();
Inst_Log("Configure file not found: %s\nCreating DEMO configure file.\n",
cfg_file);
cfgfile = fopen(cfg_file, "w");
if(!cfgfile)
{
Inst_LogTime();
Inst_Log("Could not create config file %s\n", cfg_file);
return 0;
}
fprintf(cfgfile,
"# Max acceptable connections\n"
"listen_backlog %u\n"
"\n"
"# Do we write any packet traffics to the log file?\n"
"# If yes, set it to 1, this may let the log file become very big.\n"
"log_traffic 0\n"
"\n"
"# Set a forwarding rule, the parameters is:\n"
"# redirect <port>, <destination ip>:<destination port>, , , \n"
"# Default MTU is %u\n"
"# Here comes an example\n"
"redirect 80, 192.168.1.105:80, 1, 8192, 8192\n",
backlog_default, def_packet_size);
fclose(cfgfile);
return 0;
}
pInst->listen_backlog = backlog_default;
pInst->log_traffic = 0;
do
{
int n1, n2, n3, n4, n5, n6, n7, n8, n9;
int fields;
if(!fgets(LineBuf, sizeof(LineBuf), cfgfile))
break;
LineNo++;
pChr = LineBuf;
while(isspace(*pChr))
pChr++;
if(*pChr == '#') // Comment
continue;
if(sscanf(pChr, "listen_backlog %d", &n1) == 1)
{
pInst->listen_backlog = n1;
Inst_LogTime();
Inst_Log("listen backlog = %d\n", n1);
}
else if(sscanf(pChr, "log_traffic %d", &n1) == 1)
{
pInst->log_traffic = n1;
Inst_LogTime();
Inst_Log("log traffic = %d\n", n1);
}
else
{
n7 = 0;
n8 = n9 = def_packet_size;
fields = sscanf(pChr, "redirect %d,%d.%d.%d.%d:%d,%d,%d,%d",
&n1, &n2, &n3, &n4, &n5, &n6, &n7, &n8, &n9);
if(fields >= 6)
{
fwdroute_p pnew = _Inst_AddRoute(pInst, n1,
_IpAddrV4ByNums(n2, n3, n4, n5), n6, n7, n8, n9);
if(!pnew)
{
Inst_LogTime();
Inst_Log("Add redirect option failed at line %u\n", LineNo);
}
}
}
}while(!feof(cfgfile));
fclose(cfgfile);
if(!pInst->num_route)
{
Inst_LogTime();
Inst_Log("No routes added. Quitting.\n");
return 0;
}
return 1;
}
//==============================================================================
//Func: Inst_Term
//Desc: Terminate instance, clear memory
//------------------------------------------------------------------------------
void Inst_Term(fwdinst_p pInst)
{
Inst_LogTime();
Inst_Log(EXEC_NAME" stopped.\n\n");
if(pInst)
{
size_t i;
for(i = 0; i < pInst->num_route; i++)
{
size_t j;
for(j = 0; j < pInst->pRoute.num_fwd_conn; j++)
{
_Inst_BreakConnection(&pInst->pRoute, j);
}
free(pInst->pRoute.fwd_conn);
}
free(pInst->pRoute);
free(pInst);
}
}
//==============================================================================
//Func: Inst_Run
//Desc: Run instance, do forwarding
//------------------------------------------------------------------------------
int Inst_Run(fwdinst_p pInst)
{
size_t i;
int rv = 0;
if(pInst->max_route > pInst->num_route)
{
pInst->max_route = pInst->num_route;
pInst->pRoute = realloc(pInst->pRoute,
pInst->max_route * sizeof(fwdroute_t));
}
for(i = 0; i < pInst->num_route; i++)
{
fwdroute_p pRoute = &pInst->pRoute;
address_t addr_from;
socklen_t cb_addr_from = sizeof(addr_from.sa);
int sockfd;
size_t j;
sockfd = accept(pRoute->listen_socket, &addr_from.sa, &cb_addr_from);
if(sockfd == -1)
{
int Err = socket_errno;
switch(Err)
{
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
case EPERM:
Inst_LogTime();
Inst_Log("Accept connection failed: firewall rules forbid connection.\n");
break;
default:
Inst_LogTime();
Inst_Log("Accept connection failed: %d.\n", Err);
break;
}
}
else
{
rv = 1;
if(_SetSocketBlockingEnabled(sockfd, 0))
{
if(addr_from.sa.sa_family == AF_INET)
{
fwdconn_p pConn;
Inst_LogTime();
Inst_Log("Accepted new connection from ");
Inst_LogAddrV4(&addr_from);
Inst_Log("\n");
pConn = _Inst_AddNewConnection(pRoute, sockfd, &addr_from, cb_addr_from);
if(!pConn)
{
closesocket(sockfd);
break;
}
}
else
;//TODO: Add IPv6 support
}
}
for(j = 0; j < pRoute->num_fwd_conn; j++)
{
size_t cbSendSize;
//=================================================================
//Receive data from source
//-----------------------------------------------------------------
if(pRoute->fwd_conn.cb_src < fwd_buffer_size)
{
ssize_t cbRecv = recv(pRoute->fwd_conn.sockfd_src,
(char*)pRoute->fwd_conn.buffer_src
+ pRoute->fwd_conn.cb_src,
fwd_buffer_size - pRoute->fwd_conn.cb_src, 0);
if(cbRecv > 0)
{
rv = 1;
pRoute->fwd_conn.cb_src += cbRecv;
if(pInst->log_traffic)
{
Inst_LogTime();
Inst_Log("Received %u bytes from source ", cbRecv);
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
}
}
else if(cbRecv == 0)
{
Inst_LogTime();
Inst_Log("Lost connection from source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
else if(cbRecv < 0)
{
int Err = socket_errno;
switch(Err)
{
case EINPROGRESS:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
case ECONNABORTED:
case ENOTCONN:
Inst_LogTime();
Inst_Log("Lost connection from source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNRESET:
Inst_LogTime();
Inst_Log("Connection reset by source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNREFUSED:
Inst_LogTime();
Inst_Log("Connection refused by source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
default:
Inst_LogTime();
Inst_Log("An error occured (%d) while receiving data from "
"source ", Err);
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
}
}
//=================================================================
//Send data to destination
//-----------------------------------------------------------------
if(pRoute->fwd_conn.cb_src > 0 )
{
ssize_t cbSend;
cbSendSize = pRoute->fwd_conn.cb_src;
if(cbSendSize > pRoute->packet_size_to_dst)
cbSendSize = pRoute->packet_size_to_dst;
cbSend = send(pRoute->fwd_conn.sockfd_dst,
pRoute->fwd_conn.buffer_src,
cbSendSize, MSG_NOSIGNAL);
if(cbSend > 0 && cbSend <= pRoute->fwd_conn.cb_src)
{
rv = 1;
pRoute->fwd_conn.cb_src -= cbSend;
if(pRoute->fwd_conn.cb_src)
{
memmove(pRoute->fwd_conn.buffer_src,
(char*)pRoute->fwd_conn.buffer_src + cbSend,
pRoute->fwd_conn.cb_src);
}
if(!pRoute->fwd_conn.connected)
{
Inst_LogTime();
Inst_Log("Connected to destination: ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
pRoute->fwd_conn.connected = 1;
}
if(pInst->log_traffic)
{
Inst_LogTime();
Inst_Log("Sent %u bytes from source ", cbSend);
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log(" to destination ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
}
}
else if(cbSend < 0)
{
int Err = socket_errno;
switch(Err)
{
case EINPROGRESS:
case ENOTCONN:
// Inst_Log("c");
pRoute->fwd_conn.connected = 0;
break;
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
case ECONNABORTED:
Inst_LogTime();
Inst_Log("Connection aborted by destination: ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNRESET:
Inst_LogTime();
Inst_Log("Connection reset by destination: ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNREFUSED:
Inst_LogTime();
Inst_Log("Connection refused by destination: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
default:
Inst_LogTime();
Inst_Log("An error occured (%d) while sending data to destination ", Err);
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
}
}
//=================================================================
//Receive data from destination
//-----------------------------------------------------------------
if(pRoute->fwd_conn.cb_dst < fwd_buffer_size)
{
ssize_t cbRecv = recv(pRoute->fwd_conn.sockfd_dst,
(char*)pRoute->fwd_conn.buffer_dst
+ pRoute->fwd_conn.cb_dst,
fwd_buffer_size - pRoute->fwd_conn.cb_dst, 0);
if(cbRecv > 0)
{
rv = 1;
pRoute->fwd_conn.cb_dst += cbRecv;
if(pInst->log_traffic)
{
Inst_LogTime();
Inst_Log("Received %u bytes from destination ", cbRecv);
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
}
}
else if(cbRecv == 0)
{
Inst_LogTime();
Inst_Log("Lost connection from source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
else if(cbRecv < 0)
{
int Err = socket_errno;
switch(Err)
{
case EINPROGRESS:
case ENOTCONN:
pRoute->fwd_conn.connected = 0;
break;
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
case ECONNABORTED:
Inst_LogTime();
Inst_Log("Connection aborted by destination: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNRESET:
Inst_LogTime();
Inst_Log("Connection reset by destination: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNREFUSED:
Inst_LogTime();
Inst_Log("Connection refused by destination: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
default:
Inst_LogTime();
Inst_Log("An error occured (%d) while receiving data from destination ", Err);
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
}
}
//=================================================================
//Send data to source
//-----------------------------------------------------------------
if(pRoute->fwd_conn.cb_dst > 0 )
{
ssize_t cbSend;
cbSendSize = pRoute->fwd_conn.cb_dst;
if(cbSendSize > pRoute->packet_size_to_src)
cbSendSize = pRoute->packet_size_to_src;
cbSend = send(pRoute->fwd_conn.sockfd_src,
pRoute->fwd_conn.buffer_dst,
cbSendSize, MSG_NOSIGNAL);
if(cbSend > 0 && cbSend <= pRoute->fwd_conn.cb_dst)
{
rv = 1;
pRoute->fwd_conn.cb_dst -= cbSend;
if(pRoute->fwd_conn.cb_dst)
{
memmove(pRoute->fwd_conn.buffer_dst,
(char*)pRoute->fwd_conn.buffer_dst + cbSend,
pRoute->fwd_conn.cb_dst);
}
if(pInst->log_traffic)
{
Inst_LogTime();
Inst_Log("Sent %u bytes from destination ", cbSend);
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log(" to source ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
}
}
else if(cbSend < 0)
{
int Err = socket_errno;
switch(Err)
{
case EINPROGRESS:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
break;
case ENOTCONN:
case ECONNABORTED:
Inst_LogTime();
Inst_Log("Connection aborted by source: ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNRESET:
Inst_LogTime();
Inst_Log("Connection reset by source: ");
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
case ECONNREFUSED:
Inst_LogTime();
Inst_Log("Connection refused by source: ");
Inst_LogAddrV4(&pRoute->fwd_conn.addr_from);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
default:
Inst_LogTime();
Inst_Log("An error occured (%d) while sending data to source ", Err);
Inst_LogAddrV4(&pRoute->DestAddr);
Inst_Log("\n");
_Inst_BreakConnection(pRoute, j);
goto BreakLoop;
}
}
}
}
}
BreakLoop:
return rv;
}
//==============================================================================
//Func: _Inst_Daemonize
//Desc: Make program run as a daemon
//------------------------------------------------------------------------------
static int _Inst_Daemonize()
{
#ifndef WIN32
int i,lfp;
char str;
if(getppid() == 1)
{
fprintf(stderr, EXEC_NAME" was still running.\n");
return 0;
}
i = fork();
if(i < 0)
{
// fork error
return 0;
}
if(i > 0)
{
// Parent exits, child (daemon) continues
return 0;
}
setsid(); // obtain a new process group
fclose(stdin);
fclose(stdout);
fclose(stderr);
lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
if(lfp<0)
{
// Could not open lock file.
Inst_LogTime();
Inst_Log("Open lock file failed.\n");
return 0;
}
if(lockf(lfp, F_TLOCK, 0) < 0)
{
// can not lock
Inst_LogTime();
Inst_Log("Lock lock file failed.\n");
return 0;
}
// first instance continues
sprintf(str,"%d\n",getpid());
write(lfp,str,strlen(str)); // record pid to lockfile
signal(SIGCHLD,SIG_IGN); // ignore child
signal(SIGTSTP,SIG_IGN); // ignore tty signals
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGPIPE,SIG_IGN);
signal(SIGHUP,_signal_handler); // catch hangup signal
#endif
signal(SIGTERM,_signal_handler); // catch kill signal
return 1;
}
//==============================================================================
//Func: main
//Desc: Entry point
//------------------------------------------------------------------------------
int main(int argc, char**argv)
{
fwdinst_p pInst;
bool_t DoDaemonize = 0;
char*sz_cfg_file = CFG_FILE;
#ifdef WIN32
{
WSADATA wsaData;
WSAStartup(WINSOCK_VERSION, &wsaData);
}
#else
{
int c;
for(;;)
{
c = getopt(argc, argv, "c:dvh");
if(c == -1)
break;
switch(c)
{
case'c'://Customize config file
sz_cfg_file = optarg;
break;
case'd'://Run as a daemon
DoDaemonize = 1;
_g_LogToScreen = 0;
break;
default:
fprintf(stderr, "Unknown option '%c'\n", c);
case'h':
fprintf(stderr, "Usage:\n"
EXEC_NAME" [-c cfgfile][-d][-v][-h]\n"
" -c: set config file"
" -d: run as a daemon\n"
" -h: show this help\n");
break;
}
}
}
#endif
pInst = Inst_Create(DoDaemonize, sz_cfg_file);
if(pInst)
{
bo_t bo;
bo_reset(&bo);
while(!_g_Term)
{
if(Inst_Run(pInst))
bo_reset(&bo);
else
bo_update(&bo);
}
Inst_Term(pInst);
}
#ifdef WIN32
WSACleanup();
#endif
return 0;
}编译方法:写makefile
CC=gcc -std=gnu99
LD=gcc -std=gnu99
CCFLAGS=@CCFLAGS@
tcpfwd: tcpfwd.o
$(LD) -o $@ $^
.PHONY: clean
clean:
rm -f tcpfwd *.o然后make就行。CentOS下可用。
用法:
1、它会在当前目录找配置文件,找不到的话会自动生成一个模板,照着模板改就行。
2、命令行:
-c 指定自己的配置文件路径
-d 作为守护进程运行。也就是“呆萌”Daemon。没有这个选项的时候并不作为守护进程运行。
-v 作为守护进程运行的时候,打印Log到屏幕,不推荐设置它,否则神烦。
开发完以后,接下来设置CentOS连接VPN。
先安装 ppp 和 pptp:
# yum -y install ppp pptp
然后配置VPN的用户名和密码。
# vi /etc/ppp/chap-secrets
假设你的用户名是foo,密码是bar,你这么写:foo PPTP bar *然后保存。
设置VPN服务器的地址和加密,此时编辑另一个文件。
# vi /etc/ppp/peers/连接名
其中连接名可以换成你要的名字,随便取。
它的内容这样写:pty "pptp xxx.xxx.xxx.xxx --nolaunchpppd"
name foo
remotename PPTP
require-mppe-128
file /etc/ppp/options.pptp
ipparam 连接名其中的xxx.xxx.xxx.xxx是你的VPN的地址,“foo”是你的用户名,也就是之前记录在/etc/ppp/chap-secrets里面的。然后“连接名”必须和上面的一致。
写好以后保存,然后就可以准备播VPN连接了。
# modprobe nf_conntrack_pptp
# pppd call 连接名
把上面的“连接名”换成你取好的名字,然后看/var/log/messages就可以判断你连上了没。Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd: pppd 2.4.5 started by ec2-user, uid 0
Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd: Using interface ppp0
Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd: Connect: ppp0 <--> /dev/pts/0
Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pptp: anon log: The synchronous pptp option is NOT activated
Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp: anon log: Sent control packet type is 1 'Start-Control-Connection-Request'
Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp: anon log: Received Start Control Connection Reply
Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp: anon log: Client connection established.
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon log: Sent control packet type is 7 'Outgoing-Call-Request'
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon log: Received Outgoing Call Reply.
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon log: Outgoing call established (call ID 0, peer's call ID 43295).
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon log: PPTP_SET_LINK_INFO received from peer_callid 0
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon log: send_accm is 00000000, recv_accm is FFFFFFFF
Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp: anon warn: Non-zero Async Control Character Maps are not supported!
Jan 16 01:10:26 ip-xxx-xxx-xxx-xxx pppd: CHAP authentication succeeded
Jan 16 01:10:26 ip-xxx-xxx-xxx-xxx pppd: MPPE 128-bit stateless compression enabled
Jan 16 01:10:27 ip-xxx-xxx-xxx-xxx pppd: localIP address xxx.xxx.xxx.xxx
Jan 16 01:10:27 ip-xxx-xxx-xxx-xxx pppd: remote IP address 192.168.xxx.1显示为这样的就是已经连接上的了。也就是分配到了IP地址。
连接上VPN后,我需要让所有发往我的minecraft服务器的流量全部经过VPN转发,因此我需要加入路由规则。
# route add xxx.xxx.xxx.xxx ppp0
其中“xxx.xxx.xxx.xxx”是我的minecraft服务器的地址,这里先藏起来了哈。
然后这样的话,所有发往服务器的包都会经过ppp0转发,这样就能达成目的了。
设置好tcpfwd(也就是我写的那个转发器)的配置文件、启动它、然后设置好VPN和转发规则以后,我就可以在游戏的登录界面输入我的AWS的IP地址了(因为我的转发器是在AWS上运行的嘛)。
看,直连都连不上,但用了我的转发服务器后却能连上了,延迟还不高,150ms左右,赞。
这样的话,国外的用户用这个代理,也能低延迟畅玩咱们的服务器啦~~
顺带打个小广告:冒险者大陆服务器是一个办了很多年的正版服务器,游戏模式为困难难度生存模式,我们没有OP,不接收赞助,不使用命令方块,不添加MOD,因为我们相信,凭借我们的创造力,不需要多余的附加就能建立精彩的世界。
游戏QQ群号:339821483
请在加入QQ群后,通过群公告的内容,下载游戏客户端并连接我们的服务器的IP地址。
mark一下, 说不定哪天就用到了 已更新:现在这玩意儿支持用户通过配置文件来配置TCP_NODELAY了 顺手写了一个给它用的init.d的脚本,这样就可以用chkconfig将其选为系统服务,然后执行了。
但这个需要你自己找到第19行,将其中的路径改成你自己的tcpfwd的路径,我的是“cd /home/tcpfwd/”,你得改成你的。
除此以外第20行也需要改。#!/bin/sh
#
# /etc/init.d/tcpfwd
# TCP connection forwarder
#
# chkconfig: 2345 80 05
# description: TCP connection forwarder daemon
# config: /home/tcpfwd/tcpfwd.conf
# pidfile: /temp/tcpfwd.lock
# source function library
. /etc/rc.d/init.d/functions
RETVAL=0
prog="tcpfwd"
start() {
echo -n $"Starting $prog:"
cd /home/tcpfwd/
./tcpfwd -c /home/tcpfwd/tcpfwd.conf -d
RETVAL=$?
if [ "$RETVAL" = 0 ]; then
touch /var/lock/subsys/$prog
success "Starting $prog service"
else
failure "Starting $prog service"
fi
echo
}
stop() {
echo -n $"Stopping $prog:"
killproc $prog -TERM
RETVAL=$?
[ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/$prog
echo
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status $prog
RETVAL=$?
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
RETVAL=1
;;
esac
exit $RETVAL 已更新。追加了退避逻辑,降低了CPU占用,修复若干错误,并且可以控制唤醒时间了。
页:
[1]