1

本「The Linux Programming Interface」には、次のような関数 : readn があります。

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" をソケット サーバーに送信するが、"01" と "23" という 2 つのパッケージで配信される場合、readn 関数 readn(fd,buffer,4) は "01" と " 23" を "0123" に変更して 4 を返すと、アプリケーションは、その後に続くメッセージの長さが 123 バイトであることを認識します!! readn は非常に便利な機能です...

後で libevent 2.0 tutorail をググったところ、次の機能を持つエコー サーバーがあります。

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);
    }
}

関数 read_cb は、上記の readn のように各ファイル記述子のバッファを管理する代わりに、ソケット fd 受信メッセージ イベントが発生した場合に呼び出されます。それを行うより簡単な方法はありますか? つまり、次のような関数名があります。

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