【问题标题】:libevent based echo server got stuck基于 libevent 的回显服务器卡住了
【发布时间】:2021-03-09 20:43:09
【问题描述】:

这是一个基于 echoServer 的 libevent,经过我的一些调整(非常小)。

问题是,再次运行客户端模拟时,它似乎卡住了。客户端是这样做的:它打开一个到这个服务器的 TCP 连接,发送一个小数据(大约 20B)并等待回复,然后它关闭与 TCP RST 的连接,然后重复。

经过数千次(有时约为 20000 次)迭代后,客户端停止了。 Wireshark 监听 loopback 接口显示,当它停止时,最后一个 TCP 会话有数据从客户端发送,但服务器没有回显/发送回客户端,导致客户端挂起。

有什么想法吗?提前致谢!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <netinet/tcp.h>
#include <event.h>
#include <event2/listener.h>
#include <event2/bufferevent_ssl.h>

struct event_base *base;

static void echo_read_cb(struct bufferevent *bev, void *ctx) {
    /* This callback is invoked when there is data to read on bev. */
    struct evbuffer *input = bufferevent_get_input(bev);
    struct evbuffer *output = bufferevent_get_output(bev);
    
    /* Copy all the data from the input buffer to the output buffer. */
    int ret = evbuffer_add_buffer(output, input);
    if (ret) {
        printf("error happened when adding buf\n");
    }
}

static void echo_event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
        int fd = bufferevent_getfd(bev);
        evutil_closesocket(fd);
        bufferevent_free(bev);

        return;
    }
}
static void accept_error_cb(struct evconnlistener *listener, void *ctx) {
    struct event_base *base = evconnlistener_get_base(listener);
    int err = EVUTIL_SOCKET_ERROR();
    fprintf(stderr, "Got an error %d (%s) on the listener. "
            "Shutting down.\n", err, evutil_socket_error_to_string(err));
}

static void accept_conn_cb(struct evconnlistener *serv, int sock, struct sockaddr *sa,
             int sa_len, void *arg) {
    struct bufferevent *bev = bufferevent_socket_new(
            base, sock, BEV_OPT_CLOSE_ON_FREE);
    int flag = 1; 
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
    bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);

    bufferevent_enable(bev, EV_READ|EV_WRITE);
}


int main(int argc, char **argv) {
    struct evconnlistener *listener;
    struct sockaddr_in sin;

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(9999);
    sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */

    base = event_base_new();
    listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
            LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
            (struct sockaddr*)&sin, sizeof(sin));
    if (!listener) {
            perror("Couldn't create listener");
            return 1;
    }
    evconnlistener_set_error_cb(listener, accept_error_cb);
    event_base_loop(base, 0);

    evconnlistener_free(listener);
    return 0;
}

【问题讨论】:

  • 所有临时端口卡在TIME_WAIT?
  • 所有已完成的会话都以客户端发送 TCP RESET 结束。对于卡住的会话,根据wireshark的说法,TCP握手完成后,客户端将数据发送到服务器,但服务器没有将数据发送回客户端(应该回显)。在echo_read_cb 上添加了一些工具,结果服务器端认为客户端数据不是为卡住的会话而来的。我很迷惑。也许 libevent 内部的某些东西阻止了回调函数echo_read_cb 的触发。 ???

标签: c linux sockets networking


【解决方案1】:

找出问题的原因:在函数echo_event_cb中,有一个调用evutil_closesocket(fd);这不仅没有必要,而且有害!关闭套接字的调用被删除后,它可以完美地工作。

这是不必要的,因为 bufferedevent 是用 BEV_OPT_CLOSE_ON_FREE 创建的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-16
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多