1

在“The Linux Programming Interface”一书中,有一个功能:阅读如下:

ssize_t readn(int fd, void *buffer, size_t n)
{
    ssize_t numRead;                    /* # of bytes fetched by last read() */
    size_t totRead;                     /* Total # of bytes read so far */
    char *buf;

    buf = buffer;                       /* No pointer arithmetic on "void *" */
    for (totRead = 0; totRead < n; ) {
        numRead = read(fd, buf, n - totRead);

        if (numRead == 0)               /* EOF */
            return totRead;             /* May be 0 if this is first read() */
        if (numRead == -1) {
            if (errno == EINTR)
                continue;               /* Interrupted --> restart read() */
            else
                return -1;              /* Some other error */
        }
        totRead += numRead;
        buf += numRead;
    }
    return totRead;                     /* Must be 'n' bytes if we get here */
}

套接字客户端可能首先发送:0123 告诉套接字服务器消息是 123 字节!我通过以下方式使用 readn :

while(1){
    iRead = readn(fd,buffer,4) ;
    if(iRead <= 0)
        break ;
    buffer[4]=0x00;
    iMsglen = atoi(buffer) ;
    iRead = readn(fd,buffer,iMsglen) ;
    if(iRead <= 0)
        break ;
    //do something for buffer 
} //while 

如果套接字客户端首先向套接字服务器发送“0123”,但如果以 2 个包交付,即“01”和“23”,则 readn 函数 readn(fd,buffer,4) 将确保将“01”和“ 23" 到 "0123" 并返回 4,然后应用程序知道后面的消息长度为 123 字节!readn 是一个非常有用的功能...

后来我用谷歌搜索了 libevent 2.0 教程,有一个回显服务器,它具有以下功能:

void read_cb(struct bufferevent *bev, void *arg)
{
    #define MAX_LINE    256
    char line[MAX_LINE+1];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);

    while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
        line[n] = '\0';
        printf("fd=%u, read line: %s\n", fd, line);

        bufferevent_write(bev, line, n);
    }
}

如果发生任何套接字 fd 接收消息事件,则调用函数 read_cb ,而不是像上面的 readn 那样管理每个文件描述符的缓冲区来实现,有没有更简单的方法来做到这一点?我的意思是,函数名称类似于:

bufferevent_readn(bev, line, 4) 

如果我的示例的网络包,“01”发送到服务器,在“23”之后,由于 bufferevent_readn 的第 3 个参数是 4,那么“01”接收到它可能会忽略该事件,直到它接收到所有 4 个字节客户端然后拍摄事件,例如在 libevent 的 API 中构建 readn 函数!

任何建议,评论表示赞赏!

编辑 :

在我更详细地研究了 libevent 文档之后,我认为 libevent 真的很棒!

http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html

有几个函数可以做正确的工作,如以下来源所示:

bufferevent_setwatermark 和 evbuffer_get_length 和 evbuffer_drain 正是我所需要的......感谢您的出色工作!

struct info {
    const char *name;
    size_t total_drained;
};

void read_callback(struct bufferevent *bev, void *ctx)
{
    struct info *inf = ctx;
    struct evbuffer *input = bufferevent_get_input(bev);
    size_t len = evbuffer_get_length(input);
    if (len) {
        inf->total_drained += len;
        evbuffer_drain(input, len);
        printf("Drained %lu bytes from %s\n",
         (unsigned long) len, inf->name);
    }
}

void event_callback(struct bufferevent *bev, short events, void *ctx)
{
    struct info *inf = ctx;
    struct evbuffer *input = bufferevent_get_input(bev);
    int finished = 0;

    if (events & BEV_EVENT_EOF) {
        size_t len = evbuffer_get_length(input);
        printf("Got a close from %s.  We drained %lu bytes from it, "
        "and have %lu left.\n", inf->name,
        (unsigned long)inf->total_drained, (unsigned long)len);
        finished = 1;
    }
    if (events & BEV_EVENT_ERROR) {
        printf("Got an error from %s: %s\n",
        inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
        finished = 1;
    }
    if (finished) {
        free(ctx);
        bufferevent_free(bev);
    }
}

struct bufferevent *setup_bufferevent(void)
{
    struct bufferevent *b1 = NULL;
    struct info *info1;

    info1 = malloc(sizeof(struct info));
    info1->name = "buffer 1";
    info1->total_drained = 0;

    /* ... Here we should set up the bufferevent and make sure it gets
    connected... */

    /* Trigger the read callback only whenever there is at least 128 bytes
       of data in the buffer. */
    bufferevent_setwatermark(b1, EV_READ, 128, 0);

    bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);

    bufferevent_enable(b1, EV_READ); /* Start reading. */
    return b1;
}

编辑2:

http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html

evbuffer_pullup 函数允许您查看缓冲区中的数据!

int parse_socks4(struct evbuffer *buf, ev_uint16_t *port, ev_uint32_t *addr)
{
    /* Let's parse the start of a SOCKS4 request!  The format is easy:
     * 1 byte of version, 1 byte of command, 2 bytes destport, 4 bytes of
     * destip. */
    unsigned char *mem;

    mem = evbuffer_pullup(buf, 8);

    if (mem == NULL) {
        /* Not enough data in the buffer */
        return 0;
    } else if (mem[0] != 4 || mem[1] != 1) {
        /* Unrecognized protocol or command */
        return -1;
    } else {
        memcpy(port, mem+2, 2);
        memcpy(addr, mem+4, 4);
        *port = ntohs(*port);
        *addr = ntohl(*addr);
        /* Actually remove the data from the buffer now that we know we
           like it. */
        evbuffer_drain(buf, 8);
        return 1;
    }
}

编辑3:

该网页有我需要的确切样本,是这篇文章的完美样本!!!!

int get_record(struct evbuffer *buf, size_t *size_out, char **record_out)
{
    /* Let's assume that we're speaking some protocol where records
    contain a 4-byte size field in network order, followed by that
    number of bytes.  We will return 1 and set the 'out' fields if we
    have a whole record, return 0 if the record isn't here yet, and
    -1 on error.  */
    size_t buffer_len = evbuffer_get_length(buf);
    ev_uint32_t record_len;
    char *record;

    if (buffer_len < 4)
        return 0; /* The size field hasn't arrived. */

    /* We use evbuffer_copyout here so that the size field will stay on
     the buffer for now. */
    evbuffer_copyout(buf, &record_len, 4);
    /* Convert len_buf into host order. */
    record_len = ntohl(record_len);
    if (buffer_len < record_len + 4)
        return 0; /* The record hasn't arrived */

    /* Okay, _now_ we can remove the record. */
    record = malloc(record_len);
    if (record == NULL)
        return -1;

    evbuffer_drain(buf, 4);
    evbuffer_remove(buf, record, record_len);

    *record_out = record;
    *size_out = record_len;
    return 1;
}
4

0 回答 0