linux下多线程网络通信的服务端问题

自己写的linux下的一个基于多线程网络通信的服务端程序已经上线很长时间。如果客户端连接次数到达一定量值时,这个服务端就会死掉,产生core文件。

具体实现模型如下:

主线程循环等待客户端连接:
while (main_loop)
  {
    tmv.tv_sec = 5;
    tmv.tv_usec = 0;

    memcpy(&g_listener.read_set, &g_listener.active_set, sizeof(fd_set));
    if ((ns=select(g_listener.ns, &g_listener.read_set, NULL, NULL, &tmv)) < 0)
    {
      if (errno == EINTR)
        continue;

      goto err;
    }
    if (ns == 0)
    {
      /* timeout */
      continue;
    }

    if (FD_ISSET(g_listener.sd, &g_listener.read_set))
    {
re_accept:
      slen = sizeof(ws);
      if ((sd=accept(g_listener.sd, (struct sockaddr *)&ws, &slen)) < 0)
      {
        if (errno == EINTR)
          goto re_accept;
        continue;
      }

      xlog_debug1("new connection accepted, sd=%d ...", sd);

      pthread_mutex_lock(&g_ws_mutex);
      for (i=0; i<g_max_links; i++)
      {
        if (g_wst[i].ws_thrid == 0)
          break;
      }
      pthread_mutex_unlock(&g_ws_mutex);

      if (i >= g_max_links)
      {
        close(sd);
        continue;
      }

      /* create thread, ws_server_thread */
      if ((ec=pthread_create(&thrid, &attr, (void *)ws_server_thread, &sd)))
      {
        close(sd);
        xlog_error(__FILE__, __LINE__, "pthread_create(): %s", strerror(ec));
 continue;
      }

      xlog_debug1("new thread %u for socket %d created ...", thrid, sd);

      pthread_mutex_lock(&g_ws_mutex);
      g_wst[i].ws_thrid = thrid;
      pthread_mutex_unlock(&g_ws_mutex);

      /* save the socket id */
    }
  }

后来通过GDB调试发现关于共享内存的一个全局标志位被改变了,而我程序中并未主观或者显式地去修改这一变量。接着调试,发现这一变量前面是一个全局数组,而如果数组发生越界,这一问题就说通了。查看代码,一个出错处理不当导致数组越界!OK,原因找到了。

但是为什么被连接次数要达到一定量值时,服务端才会出现这种情况,而几次或者几十次根本没事,或者有时根本就不会死。后来发现很多次那个出错处理都对,但是某一情况下出错处理不对,这一特殊情况就是:主线程比子线程运行慢时。好吧,找到问题所在了。子线程正确执行有个前提:主线程的存储线程ID必须先于子线程执行,因为主线程要控制这些子线程,存储它们的线程ID。而我以前姑且以为主线程一般要比子线程快点,程序跑几次不出错,就得出这样的道理,太不严谨太不对了。

解决办法:子线程添加个等待主线程的函数,具体实现是调用pthread_cond_timedwait(),而通过sleep和select函数实现虽说也可以,但是在某些情况下不适合。

后来添加后又出现新的问题。即每次速度连接2次,每个传入子线程的参数都一样了。查看代码,的却,子线程开始时,我便等待,直至主线程下的一些变量已经改变,我才将通过指针取到传入的变量,当然会出现错误。

改完后的子线程模型如下:循环处理请求
/* connected socket descriptor */
  sd = *((int *)arg);
  
  /* wait 1 second for being putting my id into the queue by the main thread */
  thread_sleep(1, 0);
  
  /* set to noblock mode */
  if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
  {
    xlog_error(__FILE__, __LINE__, "fcntl(): %s", strerror(errno));
  } 
  
  xlog_debug1("head shmm info: flag=%x, nseg=%d", g_shinf.flg, g_shinf.nseg);
  xlog_debug1("int [%u] thread, sd:%d", pthread_self(), sd);
  
  pthread_mutex_lock(&g_ws_mutex);
  for (n = 0; n < MANAGER_MAX_LINKS; n++)
  {
    if (g_wst[n].ws_thrid == pthread_self())
      break;
  }   
  pthread_mutex_unlock(&g_ws_mutex);
  
  if (n >= MANAGER_MAX_LINKS)
  {
    close(sd);
    xlog_debug1("not found myself:%u in the queue of all threads.", pthread_self());
    return;
  } 
  
  pthread_mutex_init(&g_wst[n].mutex, NULL);
  pthread_cond_init(&g_wst[n].cond, NULL);
  
  inited = 1;
  while (g_ws_server_loop)
  {
    tmv.tv_sec  = 5;
    tmv.tv_usec = 0;
    FD_ZERO(&rdset);
    FD_SET(sd, &rdset);

    xlog_debug1("[%u] [sd:%d] selecting...", pthread_self(), sd);
    if ((ns = select(sd+1, &rdset, NULL, NULL, &tmv)) < 0)
    {
      if (errno == EINTR)
        continue;

      break;
    }

    /* timeout, to next loop */
    if (ns == 0)
      continue;

    if (!FD_ISSET(sd, &rdset))
      continue;

    xlog_debug1("[%u] workstation command arrival ...", pthread_self());

    /* receive the workstation command */
to_recv1:
    if ((rc=recv(sd, buf, MAX_COMMAND_BUF_SIZE, 0)) < 0)
    {
      if (errno == EINTR || errno == EAGAIN)
        goto to_recv1;
      if (errno == EPIPE)
        break;

      break;
    }

    xlog_debug1("[%u] %d bytes received ...", pthread_self(), rc);

    /* peer closed */
    if (rc == 0)
      break;

    buf[rc] = '\0';
    /* process command */
    sz = manager_command(buf);
    p  = buf;

    while (sz > 0)
    {
      /* to send buf */
      if ((rc=send(sd, p, sz, MSG_NOSIGNAL)) < 0)
      {
        if (errno == EINTR || errno == EAGAIN)
          continue;
        goto err_exit;
      }

      if (rc == 0)
        goto err_exit;
   
      p  += rc;
      sz -= rc;
    } 
  } 

  close(sd);
    
  xlog_debug1("[%u] [sd:%u]thread will be exited.", pthread_self(), sd);

  pthread_exit(NULL); 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值