libutp使用

简介

libutp是bittorrent开源的可靠udp

注意事项

1、客户端主动调用utp_close时,服务端会回调到on_state_change, state为UTP_STATE_EOF,此时服务端知道客户端已断开,即使此时不关闭utp_socket,后面会通过处理icmp消息回调到onError,此时同样可以关闭调utp_socket
2、客户端异常断连。其中包括程序异常退出,网线被拔等情况,会经历29s的生存时间检测,29s后发送的保活消息得不到回复时回调到onError,在关闭utp客户端资源。29s可以在utp_internal.cpp看到(KEEPALIVE_INTERVAL),可自行修改为10s,15s在进行编译成库
3、最重要的是,utp所有的接口为线程不安全的,需要在一个线程调用
4、utp最大可同时接收3000个,超过3000个不会在接收
5、on_firewall可以屏蔽掉不想接收的ip,当客户端连接时最先回调到此函数

服务端
/*************************************************************************
    > File Name: utp_server.cc
    > Author: hsz
    > Brief:
    > Created Time: Mon 24 Oct 2022 10:39:14 AM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
#include <netdb.h>
#include <signal.h>

#ifdef __linux__
#include <linux/errqueue.h>
#include <netinet/ip_icmp.h>
#endif

#include <utp/utp.h>
#include <log/log.h>

#define LOG_TAG "utp_server"

utp_context *ctx;
utp_socket *sock;
bool gExitFlag = false;
int fd;
const char *gLocalAddr = "127.0.0.1";
uint16_t gLocalPort = 8000;

void die(const char *fmt, ...)
{
    va_list ap;
    fflush(stdout);
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    exit(1);
}

void pdie(const char *err)
{
    fflush(stdout);
    perror(err);
    exit(1);
}

void hexdump(const void *p, size_t len)
{
    int count = 1;
    const uint8_t *ptr = static_cast<const uint8_t *>(p);

    while (len--)
    {
        if (count == 1)
            fprintf(stderr, "    %p: ", p);

        fprintf(stderr, " %02x", *ptr & 0xff);
        ++ptr;

        if (count++ == 16)
        {
            fprintf(stderr, "\n");
            count = 1;
        }
    }

    if (count != 1)
        fprintf(stderr, "\n");
}

void handler(int number)
{
    LOGD("caught signal\n");
    if (sock)
        utp_close(sock);
    gExitFlag = true;
}

uint64 callback_on_read(utp_callback_arguments *a)
{
    const unsigned char *p;
    ssize_t len, left;

    left = a->len;
    p = a->buf;

    while (left)
    {
        len = write(STDOUT_FILENO, p, left);
        left -= len;
        p += len;
        LOGD("Wrote %d bytes, %d left\n", len, left);
    }
    utp_read_drained(a->socket);
    return 0;
}

uint64 callback_on_firewall(utp_callback_arguments *a)
{
    if (sock)
    {
        LOGD("Firewalling unexpected second inbound connection\n");
        return 1;
    }

    LOGD("Firewall allowing inbound connection\n");
    return 0;
}

void write_data(void)
{
    if (!sock)
        return;

    char buf[] = "hello";
    utp_write(sock, buf, strlen(buf));
}

uint64 callback_on_accept(utp_callback_arguments *a)
{
    assert(!sock);
    sock = a->socket;
    LOGD("Accepted inbound socket %p. state = %d\n", sock);
    return 0;
}

uint64 callback_on_error(utp_callback_arguments *a)
{
    fprintf(stderr, "Error: %s\n", utp_error_code_names[a->error_code]);
    if (sock != NULL) {
        utp_close(sock);
        sock = NULL;
    }

    gExitFlag = true;
    return 0;
}

uint64 callback_on_state_change(utp_callback_arguments *a)
{
    LOGD("state %d: %s\n", a->state, utp_state_names[a->state]);
    utp_socket_stats *stats;

    switch (a->state)
    {
    case UTP_STATE_CONNECT:
    case UTP_STATE_WRITABLE:
        write_data();
        break;

    case UTP_STATE_EOF:
        LOGD("Received EOF from socket\n");
        if (a->socket == sock) {
            sock = NULL;
        }
        utp_close(a->socket);
        break;

    case UTP_STATE_DESTROYING:
        LOGD("UTP socket is being destroyed; exiting\n");

        stats = utp_get_stats(a->socket);
        if (stats)
        {
            LOGD("Socket Statistics:\n");
            LOGD("    Bytes sent:          %d\n", stats->nbytes_xmit);
            LOGD("    Bytes received:      %d\n", stats->nbytes_recv);
            LOGD("    Packets received:    %d\n", stats->nrecv);
            LOGD("    Packets sent:        %d\n", stats->nxmit);
            LOGD("    Duplicate receives:  %d\n", stats->nduprecv);
            LOGD("    Retransmits:         %d\n", stats->rexmit);
            LOGD("    Fast Retransmits:    %d\n", stats->fastrexmit);
            LOGD("    Best guess at MTU:   %d\n", stats->mtu_guess);
        }
        else
        {
            LOGD("No socket statistics available\n");
        }

        sock = NULL;
        gExitFlag = 1;
        break;
    }

    return 0;
}

uint64 callback_sendto(utp_callback_arguments *a)
{
    struct sockaddr_in *sin = (struct sockaddr_in *)a->address;

    LOGD("sendto: %zd byte packet to %s:%d%s\n", a->len, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
         (a->flags & UTP_UDP_DONTFRAG) ? "  (DF bit requested, but not yet implemented)" : "");

    // hexdump(a->buf, a->len);

    sendto(fd, a->buf, a->len, 0, a->address, a->address_len);
    return 0;
}

uint64 callback_log(utp_callback_arguments *a)
{
    fprintf(stderr, "log: %s\n", a->buf);
    return 0;
}

#ifdef __linux__
void handle_icmp()
{
    while (1)
    {
        unsigned char vec_buf[4096], ancillary_buf[4096];
        struct iovec iov = {vec_buf, sizeof(vec_buf)};
        struct sockaddr_in remote;
        struct msghdr msg;
        ssize_t len;
        struct cmsghdr *cmsg;
        struct sock_extended_err *e;
        struct sockaddr *icmp_addr;
        struct sockaddr_in *icmp_sin;

        memset(&msg, 0, sizeof(msg));

        msg.msg_name = &remote;
        msg.msg_namelen = sizeof(remote);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_flags = 0;
        msg.msg_control = ancillary_buf;
        msg.msg_controllen = sizeof(ancillary_buf);

        len = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);

        if (len < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                break;
            else
                pdie("recvmsg");
        }

        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
        {
            if (cmsg->cmsg_type != IP_RECVERR)
            {
                LOGD("Unhandled errqueue type: %d\n", cmsg->cmsg_type);
                continue;
            }

            if (cmsg->cmsg_level != SOL_IP)
            {
                LOGD("Unhandled errqueue level: %d\n", cmsg->cmsg_level);
                continue;
            }

            LOGD("errqueue: IP_RECVERR, SOL_IP, len %zd\n", cmsg->cmsg_len);

            if (remote.sin_family != AF_INET)
            {
                LOGD("Address family is %d, not AF_INET?  Ignoring\n", remote.sin_family);
                continue;
            }

            LOGD("Remote host: %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));

            e = (struct sock_extended_err *)CMSG_DATA(cmsg);

            if (!e)
            {
                LOGD("errqueue: sock_extended_err is NULL?\n");
                continue;
            }

            if (e->ee_origin != SO_EE_ORIGIN_ICMP)
            {
                LOGD("errqueue: Unexpected origin: %d\n", e->ee_origin);
                continue;
            }

            LOGD("    ee_errno:  %d\n", e->ee_errno);
            LOGD("    ee_origin: %d\n", e->ee_origin);
            LOGD("    ee_type:   %d\n", e->ee_type);
            LOGD("    ee_code:   %d\n", e->ee_code);
            LOGD("    ee_info:   %d\n", e->ee_info); // discovered MTU for EMSGSIZE errors
            LOGD("    ee_data:   %d\n", e->ee_data);

            // "Node that caused the error"
            // "Node that generated the error"
            icmp_addr = (struct sockaddr *)SO_EE_OFFENDER(e);
            icmp_sin = (struct sockaddr_in *)icmp_addr;

            if (icmp_addr->sa_family != AF_INET)
            {
                LOGD("ICMP's address family is %d, not AF_INET?\n", icmp_addr->sa_family);
                continue;
            }

            if (icmp_sin->sin_port != 0)
            {
                LOGD("ICMP's 'port' is not 0?\n");
                continue;
            }

            LOGD("msg_flags: %d", msg.msg_flags);

            if (msg.msg_flags & MSG_TRUNC)
                fprintf(stderr, " MSG_TRUNC");
            if (msg.msg_flags & MSG_CTRUNC)
                fprintf(stderr, " MSG_CTRUNC");
            if (msg.msg_flags & MSG_EOR)
                fprintf(stderr, " MSG_EOR");
            if (msg.msg_flags & MSG_OOB)
                fprintf(stderr, " MSG_OOB");
            if (msg.msg_flags & MSG_ERRQUEUE)
                fprintf(stderr, " MSG_ERRQUEUE");
            fprintf(stderr, "\n");
            // hexdump(vec_buf, len);

            if (e->ee_type == 3 && e->ee_code == 4)
            {
                LOGD("ICMP type 3, code 4: Fragmentation error, discovered MTU %d\n", e->ee_info);
                utp_process_icmp_fragmentation(ctx, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote), e->ee_info);
            }
            else
            {
                LOGD("ICMP type %d, code %d\n", e->ee_type, e->ee_code);
                utp_process_icmp_error(ctx, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote));
            }
        }
    }
}
#endif

void setup(void)
{
    struct sockaddr_in sin;
    int error;
    struct sigaction sigIntHandler;

    sigIntHandler.sa_handler = handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;

    sigaction(SIGINT, &sigIntHandler, NULL);

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0)
        pdie("socket");

#ifdef __linux__
    int on = 1;
    if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on)) != 0)
        pdie("setsockopt");
#endif

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(gLocalAddr);
    sin.sin_port = htons(gLocalPort);

    if (bind(fd, (sockaddr *)&sin, sizeof(sin)) != 0)
        pdie("bind");

    socklen_t len = sizeof(sin);
    if (getsockname(fd, (struct sockaddr *)&sin, &len) != 0)
        pdie("getsockname");
    LOGD("Bound to local %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));

    ctx = utp_init(2);
    assert(ctx);
    LOGD("UTP context %p\n", ctx);

    utp_set_callback(ctx, UTP_LOG, &callback_log);
    utp_set_callback(ctx, UTP_SENDTO, &callback_sendto);
    utp_set_callback(ctx, UTP_ON_ERROR, &callback_on_error);
    utp_set_callback(ctx, UTP_ON_STATE_CHANGE, &callback_on_state_change);
    utp_set_callback(ctx, UTP_ON_READ, &callback_on_read);
    utp_set_callback(ctx, UTP_ON_FIREWALL, &callback_on_firewall);
    utp_set_callback(ctx, UTP_ON_ACCEPT, &callback_on_accept);
}

void network_loop(void)
{
    unsigned char socket_data[4096];
    struct sockaddr_in src_addr;
    socklen_t addrlen = sizeof(src_addr);
    ssize_t len;
    int ret;

    struct pollfd p[2];
    p[0].fd = fd;
    p[0].events = POLLIN;

    ret = poll(p, 2, 500);
    if (ret < 0)
    {
        if (errno == EINTR)
            LOGD("poll() returned EINTR\n");
        else
            pdie("poll");
    }
    else if (ret == 0)
    {
        // LOGD("poll() timeout\n");
    }
    else
    {
#ifdef __linux__
        if ((p[0].revents & POLLERR) == POLLERR)
            handle_icmp();
#endif

        if ((p[0].revents & POLLIN) == POLLIN)
        {
            while (1)
            {
                len = recvfrom(fd, socket_data, sizeof(socket_data), MSG_DONTWAIT, (struct sockaddr *)&src_addr, &addrlen);
                if (len < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        utp_issue_deferred_acks(ctx);
                        break;
                    }
                    else
                        pdie("recvfrom");
                }

                LOGD("Received %zd byte UDP packet from %s:%d\n", len, inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
                hexdump(socket_data, len);

                if (!utp_process_udp(ctx, socket_data, len, (struct sockaddr *)&src_addr, addrlen))
                    LOGD("UDP packet not handled by UTP.  Ignoring.\n");
            }
        }
    }

    utp_check_timeouts(ctx);
}

int main(int argc, char **argv)
{
    int option = 0;
    uint16_t localPort = 8000;
    const char *localAddress = "127.0.0.1";

    while ((option = getopt(argc, argv, "hl:")) > 0)
    {
        switch (option)
        {
        case 'h': // help
            break;
        case 'l': // local port
            localPort = atoi(optarg);
            break;
        default:
            die("Unhandled argument: %c\n", option);
        }
    }

    gLocalAddr = localAddress;
    gLocalPort = localPort;

    setup();
    while (!gExitFlag)
        network_loop();

    utp_context_stats *stats = utp_get_context_stats(ctx);
    if (stats) {
		LOGD("           Bucket size:    <23    <373    <723    <1400    >1400\n");
		LOGD("Number of packets sent:  %5d   %5d   %5d    %5d    %5d\n",
			stats->_nraw_send[0], stats->_nraw_send[1], stats->_nraw_send[2], stats->_nraw_send[3], stats->_nraw_send[4]);
		LOGD("Number of packets recv:  %5d   %5d   %5d    %5d    %5d\n",
			stats->_nraw_recv[0], stats->_nraw_recv[1], stats->_nraw_recv[2], stats->_nraw_recv[3], stats->_nraw_recv[4]);
	}
	else {
		LOGD("utp_get_context_stats() failed?\n");
	}

	LOGD("Destroying context\n");
	utp_destroy(ctx);

    return 0;
}

客户端
/*************************************************************************
    > File Name: utp_client.cc
    > Author: hsz
    > Brief:
    > Created Time: Mon 24 Oct 2022 10:39:14 AM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
#include <netdb.h>
#include <signal.h>

#ifdef __linux__
#include <linux/errqueue.h>
#include <netinet/ip_icmp.h>
#endif

#include <utp/utp.h>
#include <log/log.h>

#define LOG_TAG "utp_server"

utp_context *ctx;
utp_socket *sock;
bool gExitFlag = false;
int fd;
const char *gRemoteAddr = "127.0.0.1";
uint16_t gRemotePort = 8000;

void die(const char *fmt, ...)
{
    va_list ap;
    fflush(stdout);
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    exit(1);
}

void pdie(const char *err)
{
    fflush(stdout);
    perror(err);
    exit(1);
}

void hexdump(const void *p, size_t len)
{
    int count = 1;
    const uint8_t *ptr = static_cast<const uint8_t *>(p);

    while (len--)
    {
        if (count == 1)
            fprintf(stderr, "    %p: ", p);

        fprintf(stderr, " %02x", *ptr & 0xff);
        ++ptr;

        if (count++ == 16)
        {
            fprintf(stderr, "\n");
            count = 1;
        }
    }

    if (count != 1)
        fprintf(stderr, "\n");
}

void handler(int number)
{
    LOGD("caught signal\n");
    if (sock)
        utp_close(sock);
    gExitFlag = true;
}

uint64 callback_on_read(utp_callback_arguments *a)
{
    const unsigned char *p;
    ssize_t len, left;

    left = a->len;
    p = a->buf;

    while (left)
    {
        len = write(STDOUT_FILENO, p, left);
        left -= len;
        p += len;
        LOGD("Wrote %d bytes, %d left\n", len, left);
    }
    utp_read_drained(a->socket);
    return 0;
}

uint64 callback_on_firewall(utp_callback_arguments *a)
{
    if (sock)
    {
        LOGD("Firewalling unexpected second inbound connection\n");
        return 1;
    }

    LOGD("Firewall allowing inbound connection\n");
    return 0;
}

void write_data(void)
{
    if (!sock)
        return;

    char buf[] = "hello";
    utp_write(sock, buf, strlen(buf));
}

uint64 callback_on_accept(utp_callback_arguments *a)
{
    assert(!sock);
    sock = a->socket;
    LOGD("Accepted inbound socket %p\n", sock);
    write_data();
    return 0;
}

uint64 callback_on_error(utp_callback_arguments *a)
{
    fprintf(stderr, "Error: %s\n", utp_error_code_names[a->error_code]);
    utp_close(sock);
    sock = NULL;
    gExitFlag = true;
    return 0;
}

uint64 callback_on_state_change(utp_callback_arguments *a)
{
    LOGD("state %d: %sock\n", a->state, utp_state_names[a->state]);
    utp_socket_stats *stats;

    switch (a->state)
    {
    case UTP_STATE_CONNECT:
    case UTP_STATE_WRITABLE:
        write_data();
        break;

    case UTP_STATE_EOF:
        LOGD("Received EOF from socket\n");
        utp_close(a->socket);
        break;

    case UTP_STATE_DESTROYING:
        LOGD("UTP socket is being destroyed; exiting\n");

        stats = utp_get_stats(a->socket);
        if (stats)
        {
            LOGD("Socket Statistics:\n");
            LOGD("    Bytes sent:          %d\n", stats->nbytes_xmit);
            LOGD("    Bytes received:      %d\n", stats->nbytes_recv);
            LOGD("    Packets received:    %d\n", stats->nrecv);
            LOGD("    Packets sent:        %d\n", stats->nxmit);
            LOGD("    Duplicate receives:  %d\n", stats->nduprecv);
            LOGD("    Retransmits:         %d\n", stats->rexmit);
            LOGD("    Fast Retransmits:    %d\n", stats->fastrexmit);
            LOGD("    Best guess at MTU:   %d\n", stats->mtu_guess);
        }
        else
        {
            LOGD("No socket statistics available\n");
        }

        sock = NULL;
        gExitFlag = 1;
        break;
    }

    return 0;
}

uint64 callback_sendto(utp_callback_arguments *a)
{
    struct sockaddr_in *sin = (struct sockaddr_in *)a->address;

    LOGD("sendto: %zd byte packet to %s:%d%s\n", a->len, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
         (a->flags & UTP_UDP_DONTFRAG) ? "  (DF bit requested, but not yet implemented)" : "");

    // hexdump(a->buf, a->len);

    sendto(fd, a->buf, a->len, 0, a->address, a->address_len);
    return 0;
}

uint64 callback_log(utp_callback_arguments *a)
{
    fprintf(stderr, "log: %s\n", a->buf);
    return 0;
}

#ifdef __linux__
void handle_icmp()
{
    while (1)
    {
        unsigned char vec_buf[4096], ancillary_buf[4096];
        struct iovec iov = {vec_buf, sizeof(vec_buf)};
        struct sockaddr_in remote;
        struct msghdr msg;
        ssize_t len;
        struct cmsghdr *cmsg;
        struct sock_extended_err *e;
        struct sockaddr *icmp_addr;
        struct sockaddr_in *icmp_sin;

        memset(&msg, 0, sizeof(msg));

        msg.msg_name = &remote;
        msg.msg_namelen = sizeof(remote);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_flags = 0;
        msg.msg_control = ancillary_buf;
        msg.msg_controllen = sizeof(ancillary_buf);

        len = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);

        if (len < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                break;
            else
                pdie("recvmsg");
        }

        for (cmsg = CMSG_FIRSTHDR(&msg);
             cmsg;
             cmsg = CMSG_NXTHDR(&msg, cmsg))
        {
            if (cmsg->cmsg_type != IP_RECVERR)
            {
                LOGD("Unhandled errqueue type: %d\n", cmsg->cmsg_type);
                continue;
            }

            if (cmsg->cmsg_level != SOL_IP)
            {
                LOGD("Unhandled errqueue level: %d\n", cmsg->cmsg_level);
                continue;
            }

            LOGD("errqueue: IP_RECVERR, SOL_IP, len %zd\n", cmsg->cmsg_len);

            if (remote.sin_family != AF_INET)
            {
                LOGD("Address family is %d, not AF_INET?  Ignoring\n", remote.sin_family);
                continue;
            }

            LOGD("Remote host: %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));

            e = (struct sock_extended_err *)CMSG_DATA(cmsg);

            if (!e)
            {
                LOGD("errqueue: sock_extended_err is NULL?\n");
                continue;
            }

            if (e->ee_origin != SO_EE_ORIGIN_ICMP)
            {
                LOGD("errqueue: Unexpected origin: %d\n", e->ee_origin);
                continue;
            }

            LOGD("    ee_errno:  %d\n", e->ee_errno);
            LOGD("    ee_origin: %d\n", e->ee_origin);
            LOGD("    ee_type:   %d\n", e->ee_type);
            LOGD("    ee_code:   %d\n", e->ee_code);
            LOGD("    ee_info:   %d\n", e->ee_info); // discovered MTU for EMSGSIZE errors
            LOGD("    ee_data:   %d\n", e->ee_data);

            // "Node that caused the error"
            // "Node that generated the error"
            icmp_addr = (struct sockaddr *)SO_EE_OFFENDER(e);
            icmp_sin = (struct sockaddr_in *)icmp_addr;

            if (icmp_addr->sa_family != AF_INET)
            {
                LOGD("ICMP's address family is %d, not AF_INET?\n", icmp_addr->sa_family);
                continue;
            }

            if (icmp_sin->sin_port != 0)
            {
                LOGD("ICMP's 'port' is not 0?\n");
                continue;
            }

            LOGD("msg_flags: %d", msg.msg_flags);

            if (msg.msg_flags & MSG_TRUNC)
                fprintf(stderr, " MSG_TRUNC");
            if (msg.msg_flags & MSG_CTRUNC)
                fprintf(stderr, " MSG_CTRUNC");
            if (msg.msg_flags & MSG_EOR)
                fprintf(stderr, " MSG_EOR");
            if (msg.msg_flags & MSG_OOB)
                fprintf(stderr, " MSG_OOB");
            if (msg.msg_flags & MSG_ERRQUEUE)
                fprintf(stderr, " MSG_ERRQUEUE");
            fprintf(stderr, "\n");
            // hexdump(vec_buf, len);

            if (e->ee_type == 3 && e->ee_code == 4)
            {
                LOGD("ICMP type 3, code 4: Fragmentation error, discovered MTU %d\n", e->ee_info);
                utp_process_icmp_fragmentation(ctx, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote), e->ee_info);
            }
            else
            {
                LOGD("ICMP type %d, code %d\n", e->ee_type, e->ee_code);
                utp_process_icmp_error(ctx, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote));
            }
        }
    }
}
#endif

void setup(void)
{
    struct sockaddr_in sin;
    int error;
    struct sigaction sigIntHandler;

    sigIntHandler.sa_handler = handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;

    sigaction(SIGINT, &sigIntHandler, NULL);

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd < 0)
		pdie("socket");

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    sin.sin_port = htons(9000);

    if (bind(fd, (sockaddr *)&sin, sizeof(sin)) != 0)
        pdie("bind");

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(gRemoteAddr);
    sin.sin_port = htons(gRemotePort);

    ctx = utp_init(2);
    assert(ctx);
    LOGD("UTP context %p\n", ctx);

    utp_set_callback(ctx, UTP_LOG, &callback_log);
    utp_set_callback(ctx, UTP_SENDTO, &callback_sendto);
    utp_set_callback(ctx, UTP_ON_ERROR, &callback_on_error);
    utp_set_callback(ctx, UTP_ON_STATE_CHANGE, &callback_on_state_change);
    utp_set_callback(ctx, UTP_ON_READ, &callback_on_read);
    utp_set_callback(ctx, UTP_ON_FIREWALL, &callback_on_firewall);
    utp_set_callback(ctx, UTP_ON_ACCEPT, &callback_on_accept);

    sock = utp_create_socket(ctx);
    assert(sock);
    LOGD("UTP socket %p\n", sock);

    LOGD("Connecting to %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));

    utp_connect(sock, (sockaddr *)&sin, sizeof(sin));
}

void network_loop(void)
{
    unsigned char socket_data[4096];
    struct sockaddr_in src_addr;
    socklen_t addrlen = sizeof(src_addr);
    ssize_t len;
    int ret;

    struct pollfd p[2];
    p[0].fd = STDIN_FILENO;
	p[0].events = POLLIN;

	p[1].fd = fd;
	p[1].events = POLLIN;

    ret = poll(p, 2, 500);
    if (ret < 0)
    {
        if (errno == EINTR)
            LOGD("poll() returned EINTR\n");
        else
            pdie("poll");
    }
    else if (ret == 0)
    {
        // LOGD("poll() timeout\n");
    }
    else
    {
        char buf[128] = {0};
        if ((p[0].revents & POLLIN) == POLLIN) {
			len = read(STDIN_FILENO, buf, sizeof(buf));
			if (len < 0 && errno != EINTR)
				pdie("read stdin");
			if (len == 0) {
				LOGD("EOF from file\n");
                gExitFlag = true;
				close(STDIN_FILENO);
			} else {
				LOGD("Read %d bytes\n", len);
			}
			utp_write(sock, buf, len);
		}
#ifdef __linux__
        if ((p[1].revents & POLLERR) == POLLERR)
            handle_icmp();
#endif

        if ((p[1].revents & POLLIN) == POLLIN)
        {
            while (1)
            {
                len = recvfrom(fd, socket_data, sizeof(socket_data), MSG_DONTWAIT, (struct sockaddr *)&src_addr, &addrlen);
                if (len < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        utp_issue_deferred_acks(ctx);
                        break;
                    }
                    else
                        pdie("recvfrom");
                }

                LOGD("Received %zd byte UDP packet from %s:%d\n", len, inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
                hexdump(socket_data, len);

                if (!utp_process_udp(ctx, socket_data, len, (struct sockaddr *)&src_addr, addrlen))
                    LOGD("UDP packet not handled by UTP.  Ignoring.\n");
            }
        }
    }

    utp_check_timeouts(ctx);
}

int main(int argc, char **argv)
{
    int option = 0;
    uint16_t remotePort = 8000;
    const char *remoteAddress = "127.0.0.1";

    while ((option = getopt(argc, argv, "hp:")) > 0)
    {
        switch (option)
        {
        case 'h': // help
            break;
        case 'p': // local port
            remotePort = atoi(optarg);
            break;
        default:
            die("Unhandled argument: %c\n", option);
        }
    }

    gRemoteAddr = remoteAddress;
    gRemotePort = remotePort;

    setup();
    while (!gExitFlag)
        network_loop();

    utp_context_stats *stats = utp_get_context_stats(ctx);
    if (stats) {
		LOGD("           Bucket size:    <23    <373    <723    <1400    >1400\n");
		LOGD("Number of packets sent:  %5d   %5d   %5d    %5d    %5d\n",
			stats->_nraw_send[0], stats->_nraw_send[1], stats->_nraw_send[2], stats->_nraw_send[3], stats->_nraw_send[4]);
		LOGD("Number of packets recv:  %5d   %5d   %5d    %5d    %5d\n",
			stats->_nraw_recv[0], stats->_nraw_recv[1], stats->_nraw_recv[2], stats->_nraw_recv[3], stats->_nraw_recv[4]);
	}
	else {
		LOGD("utp_get_context_stats() failed?\n");
	}

	LOGD("Destroying context\n");
	utp_destroy(ctx);

    return 0;
}

客户端通过stdin输入想要发送的内容,server端只显示收到内容,不会进行回复

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值