代码之家  ›  专栏  ›  技术社区  ›  J. Doe

C HTTP套接字过早关闭,并明显出现读取错误

  •  1
  • J. Doe  · 技术社区  · 6 年前

    td;lr:试图向HTTP客户端回显“Hello World”,但由于套接字关闭过快以及wrk基准测试工具的神秘读取错误而出现问题。

    我正在尝试使用 picoev 事件循环库,但客户端/对等连接正在过早断开,wrk基准测试工具返回读取错误,原因我不知道。这是我正在使用的代码:

    #include <assert.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <netinet/in.h>
    #include <netinet/tcp.h>
    #include <stdio.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/uio.h>
    #include <unistd.h>
    #include "picoev.h"
    
    
    #define HOST 0 /* 0x7f000001 for localhost */
    #define PORT 8080
    #define MAX_FDS 1024 * 128
    #define TIMEOUT_SECS 10
    
    char buf[1024];
    ssize_t response;
    int listen_sock;
    
    
    static void close_conn(picoev_loop* loop, int fd)
    {
      picoev_del(loop, fd);
      close(fd);
    }
    
    static void write_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
    {
      // check whether neither events nor timeouts are present
      if ((events & PICOEV_TIMEOUT) != 0) {
        /* timeout */
        close_conn(loop, fd);
    
      } else if ((events & PICOEV_READ) != 0) {
        /* update timeout, and read */
        picoev_set_timeout(loop, fd, TIMEOUT_SECS);
        ret = read(fd, buf, sizeof(buf));
    
    
        if (ret == 0 | ret == -1) {
          close_conn(loop, fd);
        }
    
        else {
          write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
          close_conn(loop, fd);
        }
    
      }
    }
    
    static void accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
    {
      int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
      if (newfd != -1) {
        picoev_add(loop, newfd, PICOEV_READ, TIMEOUT_SECS, write_callback, NULL);
      }
    }
    
    int main(void)
    {
      picoev_loop* loop;
    
      /* listen to port */
      listen_sock = socket(AF_INET, SOCK_STREAM, 0);
      setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1, sizeof(1));
      struct sockaddr_in listen_addr;
      listen_addr.sin_family = AF_INET;
      listen_addr.sin_port = htons(PORT);
      listen_addr.sin_addr.s_addr = htonl(HOST);
      bind(listen_sock, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
      listen(listen_sock, 1000000);
    
      /* init picoev */
      picoev_init(MAX_FDS);
      /* create loop */
      loop = picoev_create_loop(60);
      /* add listen socket */
      picoev_add(loop, listen_sock, PICOEV_READ, 1, accept_callback, NULL);
      /* loop */
      while (1) {
        // Picoev async call to write etc..
        picoev_loop_once(loop, 10);
      }
      /* cleanup */
      picoev_destroy_loop(loop);
      picoev_deinit();
    
      return 0;
    }
    

    使用卷曲 curl http://0.0.0.0:8080/ -v 返回值:

    *   Trying 0.0.0.0...
    * TCP_NODELAY set
    * Connected to 0.0.0.0 (127.0.0.1) port 8080 (#0)
    > GET / HTTP/1.1
    > Host: 0.0.0.0:8080
    > User-Agent: curl/7.52.1
    > Accept: */*
    > 
    < HTTP/1.1 200 OK
    < Content-Type: text/html
    < Content-Length: 13
    * transfer closed with 13 bytes remaining to read
    * Curl_http_done: called premature == 1
    * stopped the pause stream!
    * Closing connection 0
    curl: (18) transfer closed with 13 bytes remaining to read
    

    或者在多次尝试对数千个并发连接进行基准测试后执行以下操作:

    *   Trying 0.0.0.0...
    * TCP_NODELAY set
    * connect to 0.0.0.0 port 8080 failed: Connection refused
    * Failed to connect to 0.0.0.0 port 8080: Connection refused
    * Closing connection 0
    curl: (7) Failed to connect to 0.0.0.0 port 8080: Connection refused
    

    wrk -t1 -c400 http://0.0.0.0:8080/ 返回正在读取的所有错误:

    Running 10s test @ http://0.0.0.0:8080/
      1 threads and 400 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     0.00us    0.00us   0.00us    -nan%
        Req/Sec     0.00      0.00     0.00      -nan%
      0 requests in 10.08s, 9.05MB read
      Socket errors: connect 0, read 249652, write 0, timeout 0
    Requests/sec:      0.00
    Transfer/sec:      0.90MB
    

    我不明白问题是不是套接字关闭太快,响应(ret)不正确,僵尸fd没有被杀死,还是两者的结合。试图偏离程序并没有提供任何关于问题所在的有价值的信息,只是很多 epoll\u等待 我已经尝试了许多HTTP响应变体,但都无济于事,正如您所见,我正在尝试尽快杀死任何僵尸或出错的fd,但要么我做错了,要么问题出在其他地方。有人能帮我指出问题所在吗?

    1 回复  |  直到 6 年前
        1
  •  2
  •   jxh    6 年前

    在这一行代码中:

    write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);

    您使用 ret 对于调用的第三个参数 write() 。此参数用于指示 写入() 应写入多少字节。

    然而 ret公司 用于存储调用的结果 read() 。因此,传递给的值之间没有关系 写入() 以及您要发送的消息的大小。

    通过初始化修复此问题 ret公司 您要发送的消息的长度。

    const char *msg = "HTTP/1.1 ...";
    ret = strlen(msg);
    write(fd, msg, ret);