accept()调用不初始化sockaddr struct

时间:2016-06-04 15:10:29

标签: c sockets unix

我在C中编写了一个简短的TCP服务器示例,它不断侦听端口12701上的连接,并将对等方的sockaddr.sa_family的值输出到stdout。程序在无限循环中调用accept(),该循环应该用struct sockaddr填充连接的详细信息,包括sa_family。

但是,对于第一个TCP连接,这不会正确发生; sockaddr.sa_family始终为零。所有后续连接都提供正确的两个值 - 只有第一个连接错误。为什么会这样?我无法找到任何类似问题的报告,但我怀疑我正在初始化错误或错误解释accept()的参数。

以下是该计划:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(void)
{
    struct sockaddr_in saddr =
    {
        .sin_family      = AF_INET,
        .sin_addr.s_addr = htonl(INADDR_ANY),
        .sin_port        = htons(12701),
    };

    // open socket to accept() tcp connections
    int accept_fd = socket(AF_INET, SOCK_STREAM, 0);
    bind(accept_fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in));
    listen(accept_fd, SOMAXCONN);

    socklen_t addrlen;
    struct sockaddr * addr;
    for(;;)
    {
        addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
        int child_fd = accept(accept_fd, addr, &addrlen);

        // why is this always zero the first time?
        printf("addr->sa_family = %d\n", addr->sa_family);

        close(child_fd);
        free(addr);
    }

    return 0;
}

3 个答案:

答案 0 :(得分:3)

这假定AF_INET用于建立连接。

您希望将accept()传递给适合您绑定侦听套接字的结构的地址,即struct sockaddr_in

同样需要将addrlen设置为传递给accept()的地址的大小。

  for (;;)
  {
    struct sockaddr_in * addr = malloc(sizeof *addr);
    socklen_t addrlen = sizeof *addr;

    if (NULL == addr)
    {
       perror("malloc() failed");
       /* exit(), break, continue, whatever ... */
    }

    int child_fd = accept(accept_fd, (struct sockaddr *) addr, &addrlen);
    if (-1 == child_fd)
    {
       perror("accept() failed");
       free(addr);
       addr = NULL;
       /* exit(), break, continue, whatever ... */
    }

    /* Do stuff. */

    close(child_fd);
    free(addr);
  }

答案 1 :(得分:2)

未正确处理对accept()的调用。

在发布的代码中:

  1. 必须检查对accept()的调用是否失败。
  2. 必须检查对malloc()的调用是否失败
  3. 分配的内存必须是免费的
  4. 必须在每次调用addrlen之前正确初始化accept() accept()参数。
  5. 发布的代码一次只能处理一个连接。允许多个同时连接是个好主意 将每个连接路由到thread。但是,生成/销毁线程需要很长时间,所以建议有一个“游泳池”。最初设置为“未使用”的线程数并将每个连接传递给当前未使用的&#39;线程,在将该线程标记为“正在使用”之后&#39;当线程返回时,再次将其标记为“未使用”。

    在以下代码中,我们没有努力允许多个同时连接。

    for(;;)
    {
        addr = malloc(sizeof(struct sockaddr));
        if( !addr )
        {
            perror( "malloc failed" );
            exit( EXIT_FAILURE );  // exit() and EXIT_FAILURE from stdlib.h
        }
    
        // implied else, malloc successful
    
        addrlen = sizeof( struct sockaddr );
        int child_fd = accept(accept_fd, addr, &addrlen);
    
        if( -1 == child_fd )
        { // then an error occurred
            perror( "accept failed" );
        }
    
        else
        { // else, accept successful
            printf("addr->sa_family = %d\n", addr->sa_family);
            close(child_fd);
        }
    
        free( addr ); // to avoid memory leak
        addr = NULL;  // for safety
    }
    

答案 2 :(得分:0)

例如,请查看此文档:http://man7.org/linux/man-pages/man2/accept.2.html

更具体地说,是对addrlen参数的描述。 这是一个inout参数,您必须正确初始化它。

  

addrlen参数是一个value-result参数:调用者必须   初始化它以包含指向的结构的大小(以字节为单位)   通过addr;返回时它将包含对等体的实际大小   地址'。