我在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;
}
答案 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()
的调用。
在发布的代码中:
accept()
的调用是否失败。malloc()
的调用是否失败addrlen
之前正确初始化accept()
accept()
参数。发布的代码一次只能处理一个连接。允许多个同时连接是个好主意
将每个连接路由到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;返回时它将包含对等体的实际大小 地址'。