c - 连接出现错误文件描述符错误后无法接收数据

标签 c linux multithreading sockets

我正在使用 Pthreads(Linux) 在 C 中开发多线程服务器应用程序。 我的系统是双引导。Windows 7 和 Ubuntu。我重新启动了我的电脑并从 Ubuntu 启动到 Windows,在重新启动之前我的服务器应用程序工作正常。在我再次从 Windows 启动到 ubuntu 之后。并启动了我的服务器,当客户端连接时我开始收到以下错误。

recv failed: Bad file descriptor

这是我的代码:

主程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> //for socket,AF_INET,SOCK_STREAM
#include <arpa/inet.h> //for sockaddr_in
#include <string.h>   //for memset()  
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include "extern.h" 

#define MAX_CONNECTIONS  5

int main(int argc, char** argv) {

    int sock_desc = 0, connfd = 0,listenfd =0;
    struct sockaddr_in serv_addr;
    int clntSock; 
    struct sockaddr_in echoClntAddr; 
    unsigned int clntLen; 
    char sendBuff[1025];
    char recvBuff[10025];
    int n = 0;
    pthread_t thr;



    sock_desc = socket(AF_INET, SOCK_STREAM, 0); 

    if(sock_desc < 0 )
       dieWithError("Unable to open Socket\n");  

    memset(&serv_addr,0,sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET ;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(7024);

    if(bind(sock_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
       dieWithError("bind failed\n");

    if(listen(sock_desc,MAX_CONNECTIONS) < 0)
       dieWithError("listen failed\n");  

     file = fopen("testServer.txt", "w");

      clntSock = sizeof(struct sockaddr);
      int i =0;
      fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);

      while(1)
    {

        connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr,(socklen_t*)&clntSock);

      if(connfd > 0){  
         puts("Connection accepted");

        if( pthread_create( &thr, NULL ,  connection_handler , (void*) &connfd) < 0)
        {
            perror("could not create thread");
            return 1;
        }

        puts("Handler assigned");
        }    
    }

    if (connfd < 0)
    {
        perror("accept failed");
        return 1;
    }


       return (EXIT_SUCCESS);
}

void dieWithError(char *errormsg){
     printf("%s", errormsg);  
}

句柄连接.c

#include <stdio.h> 
#include <sys/socket.h> 
#include <unistd.h> 
#include <string.h>
#include "extern.h"



void *connection_handler(void *socket_desc)
{

    int sock = *(int*)socket_desc;
    t_data.t_id = sock;

    int read_size;
    char *message , client_message[2000];

    while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
    {

       client_message[read_size] = '\0';
           t_data.msg = client_message; 


           printf("%s",client_message); 

        memset(client_message, 0, 2000);
    }

    if(read_size == 0)
    {
        puts("Client disconnected");
        fflush(stdout);
    }
    else if(read_size == -1)
    {
        perror("recv failed");
    }

    return 0;
} 

我尝试重新启动我的电脑,但同样的问题仍然存在。请帮忙。

最佳答案

(您观察到的问题与在双启动机器上运行无关。)

所示代码通过对每个传入连接使用相同变量的地址来将套接字描述符传递给线程函数来引入竞争。

如果 accept() 就会发生竞争第二次会更快返回。为了让线程处理程序快速存储它通过执行指向的地址:

int sock = *(int*)socket_desc;

在您将监听套接字设置为非阻塞的特定情况下,这种情况最常见,当只有一个连接进入时,处理程序获得地址 connfd传入并并行 accept()在(非阻塞)监听套接字上再次被调用,没有传入连接正在等待并且 accept()立即返回并设置 connfd到-1。这比之前连接的处理程序调用

更快
int sock = *(int*)socket_desc;

和这家商店一起 -1 .

为了观察这个效果(不是为了解决问题)修改你的代码如下:

int result = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
if (0 > result)
{
  perror("accept() failed");
}
else
{
  connfd = result;

  puts("Connection accepted");

  if( pthread_create( &thr, NULL ,  connection_handler , (void*) &connfd) < 0)
  {
    ...

然后处理程序将像这样开始:

    void *connection_handler(void * pv)
    {
      int sock = *((int*) pv);
      fprintf("sock = %d\n", sock);
      ...

要解决您的问题,有两种方法:

  1. 滥用线程函数的 void*传递 int 的参数.

    socklen_t clntSock = ...;
    ...
    connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
    
    if (0 <= connfd)
    {
      if (pthread_create( &thr, NULL, connection_handler, (void *) connfd) < 0)
      {
        ...
    

    然后处理程序将像这样开始:

    void *connection_handler(void * pv)
    {
      int sock = (int) pv;
    
      ...
    

    这是一个常见但有点“hacky”的解决方案,它依赖于至少与 int 一样宽的指针。 .

  2. 使用一个单独的 int 实例存储 accept() 的结果.

    socklen_t clntSock = ...;
    ...
    connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
    if (0 <= connfd)
    {
      int * pconnfd = malloc(sizeof *pconnfd);
      if (NULL == pconnfd)
        ... // fail and leave here
      *pconnfd = connfd;
      if (pthread_create(&thr, NULL, connection_handler, pconnfd) < 0)
      {
        ...
    

    然后处理程序将像这样开始:

    void *connection_handler(void * pv)
    {
      int connfd = *((int *) pv);
      free(pv);
    
      ...
    

    这是一种干净且可移植的方法,需要额外调用一对 malloc()。/free() .

上面代码中的其他更正:

  • 定义地址传递给accept()的变量成为socklen_t作为 accept() 的第三个参数定义为 socklen_t * .如果将指针传递给其他东西并且这个“东西”的大小不同于 socklen_t accept()会遇到未定义的行为。

  • 测试 accept() 的结果反对<0错误为 0是文件/套接字描述符的有效值。

关于c - 连接出现错误文件描述符错误后无法接收数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30756210/

相关文章:

c - 结构数组初始化

c - 指向 VLA 的指针

.net - 如何更新不同线程中的组件? .NET CF

c++ - CPU 设备上的 OpenCL - 幕后发生了什么?

c - 读取 Ogg/Flac 文件的标签数据

c - C 中的 else if 语句与计算相结合时出现问题

linux - 如何调试作为软件中so文件一部分的cpp文件?

linux - Sed - 按最后两个字符计算行数

java - 我有这个简单的股票交易程序来计算 yield 或损失,但当我运行该程序时没有显示任何内容

Windows,多进程与多线程