network: udp

udp

// udp socket
struct mill_udpsock_ {
    int fd;
    int port;
};

// udp socket tune(设置为nonblocking)
static void mill_udptune(int s) {
    /* Make the socket non-blocking. */
    int opt = fcntl(s, F_GETFL, 0);
    if (opt == -1)
        opt = 0;
    int rc = fcntl(s, F_SETFL, opt | O_NONBLOCK);
    mill_assert(rc != -1);
}

// udp listen socket 创建(socket为非阻塞)
struct mill_udpsock_ *mill_udplisten_(ipaddr addr) {
    /* Open the listening socket. */
    int s = socket(mill_ipfamily(addr), SOCK_DGRAM, 0);
    if(s == -1)
        return NULL;
    mill_udptune(s);

    /* Start listening. */
    int rc = bind(s, (struct sockaddr*)&addr, mill_iplen(addr));
    if(rc != 0)
        return NULL;

    // 参数addr中可能没有指定port信息(用户可能希望监听指定ip、自动分配的port),
    // 此时需要获取os自动分配的port信息
    int port = mill_ipport(addr);
    if(!port) {
        ipaddr baddr;
        socklen_t len = sizeof(ipaddr);
        rc = getsockname(s, (struct sockaddr*)&baddr, &len);
        if(rc == -1) {
            int err = errno;
            fdclean(s);
            close(s);
            errno = err;
            return NULL;
        }
        port = mill_ipport(baddr);
    }

    /* Create the object. */
    struct mill_udpsock_ *us = malloc(sizeof(struct mill_udpsock_));
    if(!us) {
        fdclean(s);
        close(s);
        errno = ENOMEM;
        return NULL;
    }
    us->fd = s;
    us->port = port;
    errno = 0;
    return us;
}

// unix socket 获取绑定的port
int mill_udpport_(struct mill_udpsock_ *s) {
    return s->port;
}

// udp socket 发送(socket需提前设置为非阻塞方式)
void mill_udpsend_(struct mill_udpsock_ *s, ipaddr addr, const void *buf, size_t len) {
    struct sockaddr *saddr = (struct sockaddr*) &addr;
    ssize_t ss = sendto(s->fd, buf, len, 0, saddr, saddr->sa_family ==
        AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
    if(mill_fast(ss == (ssize_t)len)) {
        errno = 0;
        return;
    }
    mill_assert(ss < 0);
    if(errno == EAGAIN || errno == EWOULDBLOCK)
        errno = 0;
}

// udp socket 接收(socket需提前设置为非阻塞方式)
size_t mill_udprecv_(struct mill_udpsock_ *s, ipaddr *addr, void *buf, size_t len, int64_t deadline) {
    ssize_t ss;
    while(1) {
        socklen_t slen = sizeof(ipaddr);
        ss = recvfrom(s->fd, buf, len, 0, (struct sockaddr*)addr, &slen);
        if(ss >= 0)
            break;
        if(errno != EAGAIN && errno != EWOULDBLOCK)
            return 0;
        int rc = fdwait(s->fd, FDW_IN, deadline);
        if(rc == 0) {
            errno = ETIMEDOUT;
            return 0;
        }
    }
    errno = 0;
    return (size_t)ss;
}

// udp socket 关闭
void mill_udpclose_(struct mill_udpsock_ *s) {
    fdclean(s->fd);
    int rc = close(s->fd);
    mill_assert(rc == 0);
    free(s);
}

这里udp socket接收、发送之前需要自己显示地进行mill_udptune(int s),这个感觉封装地不友好。

results matching ""

    No results matching ""