UDP转发

用法: prog.exe address1:port1 address2:port2
程序将从本地端口的port1接收的数据发往 address1:port1, 同时address1:port1发出的数据将转发会原始的发送者。

#ifndef __WIN32__
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define S_CLOSE close
#define S_READ read
#define S_WRITE write
#else
#include <winsock2.h>
#define socklen_t int
#define S_CLOSE(s) closesocket(s)
#define S_READ(fd, buf, len) recv(fd, buf, len, 0)
#define S_WRITE(fd, buf, len) send(fd, buf, len, 0)
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <set>

#define UDPF_KEEP 0x00000001

struct udpiocb {
    int flags;
    int udpio_fd;
    time_t last_active;
    struct sockaddr_in udpio_addr;
};

bool operator < (const struct udpiocb & a, const struct udpiocb & b)
{
    struct sockaddr_in addr_a, addr_b;
    addr_a = a.udpio_addr;
    addr_b = b.udpio_addr;
    if (addr_a.sin_port == addr_b.sin_port)
        return (addr_a.sin_addr.s_addr < addr_b.sin_addr.s_addr);
    return (addr_a.sin_port < addr_b.sin_port);
}

static std::set<udpiocb> udpio_list;

int udpio_add(u_long addr, u_short port)
{
    int error;
    struct udpiocb iocb;
    struct sockaddr_in addr_in1;
    int s_udp = socket(PF_INET, SOCK_DGRAM, 0);
    assert(s_udp != -1);
    addr_in1.sin_family = AF_INET;
    addr_in1.sin_port = htons(port);
    addr_in1.sin_addr.s_addr = htonl(INADDR_ANY);
    error = bind(s_udp, (struct sockaddr *)&addr_in1, sizeof(addr_in1));
    assert(error == 0);
    iocb.flags = UDPF_KEEP;
    iocb.udpio_fd = s_udp;
    iocb.udpio_addr = addr_in1;
    iocb.udpio_addr.sin_addr.s_addr = addr;
    udpio_list.insert(iocb);
    return 0;
}

int udpio_final(void)
{
    std::set<udpiocb>::const_iterator iter;
    iter = udpio_list.begin();
    while (iter != udpio_list.end()) {
        S_CLOSE(iter->udpio_fd);
        ++iter;
    }
    return 0;
}

int udpio_realloc(const struct sockaddr_in & addr)
{
    struct udpiocb iocb;
    iocb.flags = 0;
    iocb.udpio_addr = addr;
    std::set<udpiocb>::iterator iter;
    iter = udpio_list.find(iocb);
    if (iter != udpio_list.end()) {
        iocb = *iter;
        time(&iocb.last_active);
        udpio_list.erase(iter);
        udpio_list.insert(iocb);
        return iocb.udpio_fd;
    }
    iocb.udpio_fd = socket(AF_INET, SOCK_DGRAM, 0);
    time(&iocb.last_active);
    udpio_list.insert(iocb);
    return iocb.udpio_fd;
}

int udpio_event(fd_set * readfds, fd_set * writefds, fd_set * errorfds)
{
    int fd, len;
       char buf[4096];
    int addr_len1;
    struct sockaddr_in addr_in1;
    std::set<udpiocb>::iterator iter;
    iter = udpio_list.begin();
    while (iter != udpio_list.end()) {
        if (FD_ISSET(iter->udpio_fd, readfds)) {
            addr_len1 = sizeof(addr_in1);
            len = recvfrom(iter->udpio_fd, buf, sizeof(buf), 0,
                     (struct sockaddr *)&addr_in1, &addr_len1);
            fd = udpio_realloc(addr_in1);
            sendto(fd, buf, len, 0, (struct sockaddr *)&(iter->udpio_addr),
                     sizeof(iter->udpio_addr));
        }
        ++iter;
    }
    return 0;
}

int udpio_collect(time_t current)
{
    int count = udpio_list.size();
    std::set<udpiocb>::iterator iter;
    iter = udpio_list.begin();
    while (iter != udpio_list.end()) {
        if (iter->last_active + 60 < current &&
                (iter->flags & UDPF_KEEP) == 0) {
            closesocket(iter->udpio_fd);
            udpio_list.erase(iter++);
            continue;
        }
         ++iter;
    }
    if (udpio_list.size() != count)
        printf("udpio collect: %d %d\n", udpio_list.size(), count);
    assert (udpio_list.size() <= count);
    return count - udpio_list.size();
}

int udpio_fd_set(fd_set * readfds, fd_set * writefds, fd_set * errorfds)
{
    int fd_max = 0;
    std::set<udpiocb>::const_iterator iter;
    iter = udpio_list.begin();
    while (iter != udpio_list.end()) {
        FD_SET(iter->udpio_fd, readfds);
        fd_max = (fd_max < iter->udpio_fd? iter->udpio_fd: fd_max);
        ++iter;
    }
    return fd_max;
}

int udp_switch(void)
{
    int count;
    struct fd_set readfds, writefds, errorfds;

    time_t t_last, t_current;
    size_t c_active = udpio_list.size();

    time(&t_last);
    for ( ; ; ) {
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);
        FD_ZERO(&errorfds);

        int max_fd = udpio_fd_set(&readfds, &writefds, &errorfds);

        struct timeval timeout = {1, 1};
        count = select(max_fd + 1, &readfds, &writefds, &errorfds, &timeout);
        if (count == -1) {
            printf("select error: %d \n", count);
            continue;
        }

        if (count == 0) {
            continue;
        }

        if (c_active != udpio_list.size() &&
                time(&t_current) != t_last) {
            udpio_collect(t_current);
            t_last = t_current;
            c_active = udpio_list.size();
        }

        printf("udpio event\n");
        udpio_event(&readfds, &writefds, &errorfds);
    }
    return 0;
}

/* udp_switch addr1:port1 */
int main(int argc, char * argv[])
{
    int error;
    char buf[512];

    WSADATA data;
       WSAStartup(0x201, &data);

    int count = 0;
    for (int i = 1; i < argc; i++) {
        char * pdot = NULL;
        strncpy(buf, argv[i], sizeof(buf));
        buf[sizeof(buf) - 1] = 0;
        pdot = strchr(buf, ':');
        if (pdot == NULL)
            continue;
        *pdot++ = 0;
        int port = atoi(pdot);
        if (port == 0 || port == -1)
            continue;
        udpio_add(inet_addr(buf), port);
        count++;
    }

    if (count > 0)
        udp_switch();
    udpio_final();
    return 0;
}

https://files.cnblogs.com/ahuo/UDPPass.rar 

原文地址:https://www.cnblogs.com/ahuo/p/1997276.html