0xAA55 发表于 2017-1-16 00:25:22

【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地址。

0x01810 发表于 2017-1-16 09:25:20

mark一下, 说不定哪天就用到了

0xAA55 发表于 2017-2-2 15:47:14

已更新:现在这玩意儿支持用户通过配置文件来配置TCP_NODELAY了

0xAA55 发表于 2017-2-8 21:00:59

顺手写了一个给它用的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

0xAA55 发表于 2019-4-25 04:43:41

已更新。追加了退避逻辑,降低了CPU占用,修复若干错误,并且可以控制唤醒时间了。
页: [1]
查看完整版本: 【C】一个跨平台TCPIP协议通讯的例子,网络包裹转发代理,非阻塞IO,单线程,可实用