【发布时间】:2014-07-04 12:53:55
【问题描述】:
我面临着我生命中最奇怪的编程问题之一。 我过去建了几台服务器,客户端可以正常连接,没有任何问题。
现在我正在创建一个基本上是 Web 服务器的服务器。但是,我面临着一个非常奇怪的情况(至少对我而言)。
假设您连接到 localhost:8080 并且 accept() 接受您的连接,然后代码将在单独的线程中处理您的请求(想法是在每个子节点上拥有多个分叉和线程 - 这是在另一个文件上实现的暂时但我在该设置上也面临这个问题所以......最好先让它简单)。因此,您的请求得到处理,但在处理完毕并关闭套接字之后,您在浏览器上看到输出,accept() 再次接受连接 - 但当然没有人连接,因为只创建了一个连接。
recv 之后的 errno = 0(成功)(这就是程序崩溃的地方) recv 虽然返回 0 - 所以没有读取字节(当然,因为连接不应该存在)
int main(int argc, char * argv[]){
int sock;
int fd_list[2];
int fork_id;
/* Socket */
sock=create_socket(PORT);
int i, active_n=0;
pthread_t tvec;
char address[BUFFSIZE];
thread_buffer t_buffer;
int msgsock;
conf = read_config("./www.config");
if(conf == NULL)
{
conf = (config*)malloc(sizeof(config));
if(conf == NULL)
{
perror("\nError allocating configuration:");
exit(-1);
}
// Set defaults
sprintf(conf->httpdocs, DOCUMENT_ROOT);
sprintf(conf->cgibin, CGI_ROOT);
}
while(cicle) {
printf("\tWaiting for connections\n");
// Waits for a client
msgsock = wait_connection(sock, address);
printf("\nSocket: %d\n", msgsock);
t_buffer.msg = &address;
t_buffer.sock = msgsock;
t_buffer.conf = conf;
/* Send socket to thread */
if (pthread_create(&tvec, NULL, thread_func, (void*)&t_buffer) != 0)
{
perror("Error creating thread: ");
exit(-1);
}
}
free(conf);
return 0;
}
这里有两个重要的函数:
int create_socket(int port) {
struct sockaddr_in server, remote;
char buffer[BUFF];
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_in))) {
perror("binding stream socket");
exit(1);
}
gethostname(buffer, BUFF);
printf("\n\tServidor a espera de ligações.\n");
printf("\tUse o endereço %s:%d\n\n", buffer,port);
if (listen(sock, MAXPENDING) < 0) {
perror("Impossível criar o socket. O servidor vai sair.\n");
exit(1);
}
return(sock);
}
int wait_connection(int serversock, char *remote_address){
int clientlen;
int clientsock;
struct sockaddr_in echoclient;
clientlen = sizeof(echoclient);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen)) < 0)
{
perror("Impossivel estabelecer ligacao ao cliente. O servidor vai sair.\n");
exit(-1);
}
printf("\n11111111111111Received request - %d\n", clientsock);
sprintf(remote_address, "%s", inet_ntoa(echoclient.sin_addr));
return clientsock;
}
所以基本上你会看到: 11111111111111收到请求 - D
D 两次都不同,所以 fd 肯定不同。
两次!一个接一个地被处理,然后在线程函数中的recv之后爆炸。有时需要一点时间来处理和显示第二个,但它会在几秒钟后完成。现在,这并不总是发生。有时会,有时不会。
这太奇怪了……
我已经推出了作为插件导致它重新连接的可能性,因为 Apache 的 ab 工具在几次请求后会导致同样的问题。
我想指出,即使我不为客户端运行线程并简单地关闭套接字,它也会发生!我已经考虑过标头未被完全读取的可能性,因此浏览器会发送另一个请求。但是浏览器会正确接收数据,否则将无法正常显示结果,如果显示结果正常,则连接必须已关闭 - 否则应出现连接重置。
有什么建议吗?感谢您的帮助。
编辑: 如果我取出代码的启动线程部分,有时连接被接受4、5、6次......
编辑2:请注意,我知道recv失败后程序会崩溃,我故意退出。
【问题讨论】:
-
我正在使用 Apache 的 ab 工具对其进行测试,我得到了相同的结果,所以我怀疑它是浏览器。至于 fork 的事情,我现在只是使用一个简单的线程服务器设置来测试它,forks 系统在其他地方有注释。
-
当对端执行有序关闭时,
recv的返回值为 0。您应该立即关闭套接字。也许您的浏览器连接了多次。 -
问题是,ab 也这样做!不只是我的浏览器。 Apache 的 ab 也可以做到这一点。发生这种情况时,我目前正在关闭套接字并退出相应的线程(在我发布的代码中,我关闭了程序以便对其进行测试)
-
你也谈到了
fork()。你确定你没有在这里复制一些东西吗? -
是的,如果您仔细查看我所说的内容(在我的帖子和下面的答案中),您会看到分叉部分已被评论,根本没有运行。您在第一篇文章中看到的大部分代码都没有包含等。
标签: c multithreading sockets