套接字服务器为某些请求返回 tcp 错误 [RST, ACK]

Posted

技术标签:

【中文标题】套接字服务器为某些请求返回 tcp 错误 [RST, ACK]【英文标题】:socket server returns tcp error [RST, ACK] for somme requests 【发布时间】:2014-09-30 16:03:04 【问题描述】:

我使用socket在linux上用c开发了一个服务器。

服务器运行正常。如果我从 Web 浏览器向服务器发出请求,服务器会毫无问题地响应请求。

但我在捕获流量(使用 wireshark)中指出,我的服务器有时会(很少)重复 tcp 错误 [RST,ACK]。

在wireshark中显示的tcp错误包之后

192.168.1.211 是我的服务器地址

我检查了“follow tcp stream”,我发现它包含 2 个请求(来自网络浏览器)。我认为它应该在给定的套接字中只包含一个请求。我说的对吗?

在follow tcp stream之后:

我不知道这是否是一个正常的错误,是否可能发生在 TCP 堆栈中?还是与我的代码有关的问题?

代码:

static void http_cr_new_client(int client, bool service_available)

    FILE *fp;
    char buffer[BUFSIZ];
    int8_t auth_status = 0;

    fp = fdopen(client, "r+");

    while (fgets(buffer, sizeof(buffer), fp)) 
        if (!strncasecmp(buffer, "Authorization: Digest ", strlen("Authorization: Digest "))) 
            char *username = conf.cpe_userid;
            char *password = conf.cpe_passwd;

            if (!username || !password) 
                // if we dont have username or password configured proceed with connecting to ACS
                service_available = false;
                goto http_end;
            

            if (http_digest_auth_check("GET", "/", buffer + strlen("Authorization: Digest "), REALM, username, password, 300) == MHD_YES)
                auth_status = 1;
            else
                auth_status = 0;
        

        if (buffer[0] == '\r' || buffer[0] == '\n') 
            /* end of http request (empty line) */
            goto http_end;
        
    
    if(!service_available) 
        goto http_end;
    

http_error:
        /* here we are because of an error, e.g. timeout */
    goto http_done;

http_end:
    if (!service_available) 
        MY_LOG (INFO,"Receive Connection Request: Return 503 Service Unavailable");
        fputs("HTTP/1.1 503 Service Unavailable\r\n", fp);
        fputs("Connection: close\r\n", fp);
     else if (auth_status) 
        MY_LOG (INFO,"Receive Connection Request: success authentication");
        fputs("HTTP/1.1 200 OK\r\n", fp);
        fputs("Content-Length: 0\r\n", fp);
        http_success_cr();
     else 
        MY_LOG (INFO,"Receive Connection Request: Return 401 Unauthorized");
        fputs("HTTP/1.1 401 Unauthorized\r\n", fp);
        fputs("Connection: close\r\n", fp);
        http_digest_auth_fail_response(fp, "GET", "/", REALM, OPAQUE);
        fputs("\r\n", fp);
    
    fputs("\r\n", fp);
http_done:
    fclose(fp);
    close(client);


void http_server_init(void)

    int socket_desc , client_sock , c , *new_sock;
    struct sockaddr_in server , client;
    static int cr_request = 0;
    static time_t restrict_start_time = 0;
    time_t current_time;
    bool service_available;

    for(;;) 
        //Create socket
        socket_desc = socket(AF_INET , SOCK_STREAM , 0);
        if (socket_desc == -1)
        
            MY_LOG (ERROR,"Could not open server socket for Connection Requests");
            sleep(1);
            continue;
        

        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(conf.connection_request_port);

        /* enable SO_REUSEADDR */
        int reusaddr = 1;
        if (setsockopt(socket_desc, SOL_SOCKET, SO_REUSEADDR, &reusaddr, sizeof(int)) < 0) 
            MY_LOG (WARNING,"setsockopt(SO_REUSEADDR) failed");
        

        //Bind
        if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
        
            //print the error message
            MY_LOG (ERROR,"Could not bind server socket for Connection Requests");
            sleep(1);
            continue;
        
        break;
    

    MY_LOG (INFO,"Connection Request server initiated");

    //Listen
    listen(socket_desc , 3);

    //Accept and incoming connection
    c = sizeof(struct sockaddr_in);
    while( (client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c)) )
    
        current_time = time(NULL);
        service_available = true;
        if ((restrict_start_time==0) ||
            ((current_time-restrict_start_time) > CONNECTION_REQUEST_RESTRICT_PERIOD))
        
            restrict_start_time = current_time;
            cr_request  = 1;
        
        else
        
            cr_request++;
            if (cr_request > 70)
            
                restrict_start_time = current_time;
                service_available = false;
            
        
        http_cr_new_client(client_sock, service_available);
    

    if (client_sock < 0)
    
        MY_LOG(ERROR,"Could not accept connections for Connection Requests!");
        return;
    

注意:我使用 pthread_create() 主启动服务器

【问题讨论】:

fclose(fp);关闭(客户端); fp 在哪里打开客户端描述符...您不是要写入已关闭的连接吗? @0d0a fclose(fp); close(client); 在读/写套接字后放在函数的末尾。所以我认为没有问题 close() 的调用将失败。来自man fdopen: The file descriptor is not dup'ed, and will be closed when the stream created by fdopen() is closed. 如果没有失败,这意味着它随机close()ed 另一个套接字描述符,可能属于另一个线程。在任何情况下,您都可能希望为调用 close() 添加错误检查。 [RST, ACK] 通常意味着以下两种情况之一:(1) 尝试连接到无人监听的端口,以及 (2) 将数据发送到关闭的连接,即您所在的连接已经发送了[FIN]。在这种情况下,原因(2)似乎很可能就是这种情况。此外,查看端口 35897 上传递的其他流量也会有所帮助。您可以使用 Wireshark 过滤器表达式将显示的数据限制在该会话中。这将为发生的事情提供一些背景信息。看到一个随机的[RST, ACK] 并不能说明我们是如何到达那里的。 @dgnuff 您无法在 FIN 之后发送数据。不可能。本地 TCP 不会让你。写入已关闭的连接意味着已被对等方关闭,无论出于何种原因。 【参考方案1】:

您似乎希望在代码中只处理一个请求。 HTTP 协议在 1.1 版本中添加了持久连接,允许将同一个 TCP 套接字重复用于多个请求。

使用持久连接,客户端(您的浏览器)将发送“Connection: keep-alive”标头以表明它支持持久连接(您的浏览器支持)。然后服务器可以回复同样的内容,或者使用“Connection: close”表示它不支持持久连接,因此客户端应该关闭当前套接字并为后续请求打开一个新套接字。

在您的情况下,浏览器打开一个套接字连接,发送一个请求并告诉您它支持持久连接。然后您的服务器发送响应并关闭连接。浏览器尝试通过同一连接发送第二个请求并失败,从而导致您看到的错误。

您可以修改代码以保持套接字打开并处理多个请求,或者添加“连接:关闭”以告诉客户端您不支持持久连接。

【讨论】:

很好的答案。谢谢

以上是关于套接字服务器为某些请求返回 tcp 错误 [RST, ACK]的主要内容,如果未能解决你的问题,请参考以下文章

TCP协议与socket套接字

unp第七章补充之socket tcp 产生 rst响应的情况

自己用C语言构造数据包,实现TCP三次握手过程,为啥中间会产生一个RST信号?

在 TCP/IP 套接字连接中发送重置

TCP之种种连接异常

TCP关闭连接 - 不容易