简体   繁体   English

Linux TCP Server问题C ++

[英]Linux TCP Server Issue C++

I have been trying to figure out this problem for over a month now. 我已经尝试解决这个问题已有一个多月了。 I have no where else to turn. 我无处可去。 I have a server that listens to many multicast channels (100ish). 我有一台服务器,可以监听许多多播通道(100ish)。 Each socket is its own thread. 每个套接字是其自己的线程。 Then I have a client listener (single threaded) that handles all incoming connections, disconnects, and client messaging within the same server. 然后,我有一个客户端侦听器(单线程),该侦听器处理同一服务器内的所有传入连接,断开连接和客户端消息传递。 The idea is that a client comes in, connects, requests data from a multicast channels and I send the data back to the client. 这个想法是客户端进入,连接,从多播通道请求数据,然后我将数据发送回客户端。 The client stays connected and I relay the UDP data back to the client. 客户端保持连接状态,我将UDP数据中继回客户端。 The client can either request UDP or TCP has the protocol for the data relay. 客户端可以请求UDP或TCP具有用于数据中继的协议。 At one point this was working beautifully for a couple of weeks. 在某一点上,它工作了好几个星期。 We did some code and kernel changes, and now we cant figure out whats gone wrong. 我们做了一些代码和内核更改,现在我们无法找出出了什么问题。

The server will run for hours and have hundreds of clients connected throughout the day. 该服务器将运行数小时,并全天连接数百个客户端。 But at some point, randomly, the server will just stop. 但是在某个时候,服务器将随机停止。 And by stop, I mean: all UDP sockets stop receiving/handling data (tcpdump shows data still coming to the box), the client_listener thread stops receiving client packets. 停止,我的意思是:所有UDP套接字都停止接收/处理数据(tcpdump显示数据仍在发送到盒子中),client_listener线程停止接收客户端数据包。 BUT!!! 但!!! the main client_listener socket can still receive new connections and new disconnects on the main socket. main client_listener主套接字仍可以在主套接字上接收新的连接和新的断开连接。 On a new connection, the main sockets is able to send a "Connection Established" packet back to the client, but then when the client responds, the select never returns. 在新连接上,主套接字能够将“已建立连接”数据包发送回客户端,但是当客户端响应时,选择永不返回。

I can post code if someone would like. 如果有人愿意,我可以发布代码。 If anyone has any suggestions where to look or if this sounds like something. 如果有人对您有何建议,或者听起来像什么。 Please let me know. 请告诉我。

If you have any questions, please ask. 如果您有任何问题,请询问。

Thank you. 谢谢。


I would like to share my TCP Server code: This is a single thread. 我想分享我的TCP Server代码:这是一个单线程。 Works fine for hours and then I will only receive "New Connections" and "Disconnects". 可正常工作数小时,然后我将仅收到“新连接”和“断开连接”。 NO CLIENT PACKETS WILL COME IN. 不会出现客户端包。

int opt = 1;
  int addrlen;
  int sd;
  int max_sd;
  int valread;
  int activity;
  int new_socket;
  char buffer[MAX_BUFFER_SIZE];
  int client_socket[m_max_clients];
  struct sockaddr_in address;

  fd_set readfds;
  for(int i = 0; i<m_max_clients; i++)
  {
    client_socket[i]=0;
  }

  if((m_master_socket = socket(AF_INET,SOCK_STREAM,0))==0)
    LOG(FATAL)<<"Unable to create master socket";

  if(setsockopt(m_master_socket,SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(opt))<0)
    LOG(FATAL)<<"Unable to set master socket";

  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;
  address.sin_port = htons(m_listenPort);

  if(bind(m_master_socket,(struct sockaddr*)& address, sizeof(address))!=0)
    LOG(FATAL)<<"Unable to bind master socket";

  if(listen(m_master_socket,SOMAXCONN)!=0)
    LOG(FATAL)<<"listen() failed with err";

  addrlen = sizeof(address);
  LOG(INFO)<<"Waiting for connections......";

while(true)
  {
    FD_ZERO(&readfds);

    FD_SET(m_master_socket, &readfds);
    max_sd = m_master_socket;

    for(int i = 0; i<m_max_clients; i++)
    {
      sd = client_socket[i];

      if(sd > 0)
        FD_SET(sd, &readfds);

      if(sd>max_sd)
        max_sd = sd;
    }

    activity = select(max_sd+1,&readfds,NULL,NULL,NULL);

    if((activity<0)&&(errno!=EINTR))
    {
    //  int err = errno;
  //    LOG(ERROR)<<"SELECT ERROR:"<<activity<<" "<<err;
      continue;
    }

    if(FD_ISSET(m_master_socket, &readfds))
    {
      if((new_socket = accept(m_master_socket,(struct sockaddr*)&address, (socklen_t*)&addrlen))<0)
        LOG(FATAL)<<"ERROR:ACCEPT FAILED!";

      LOG(INFO)<<"New Connection, socket fd is (" << new_socket << ") client_addr:" << inet_ntoa(address.sin_addr) << " Port:" << ntohs(address.sin_port);
      for(int i =0;i<m_max_clients;i++)
      {
        if(client_socket[i]==0)
        {
          //try to set the socket to non blocking, tcp nagle and keep alive
          if ( !SetSocketBlockingEnabled(new_socket, false) )
            LOG(INFO)<<"UNABLE TO SET NON-BLOCK: ("<<new_socket<<")" ;
          if ( !SetSocketNoDelay(new_socket,false) )
            LOG(INFO)<<"UNABLE TO SET DELAY: ("<<new_socket<<")" ;
//           if ( !SetSocketKeepAlive(new_socket,true) )
//            LOG(INFO)<<"UNABLE TO SET KeepAlive: ("<<new_socket<<")" ;

          ClientConnection* con = new ClientConnection(m_mocSrv, m_udpPortGenerator, inet_ntoa(address.sin_addr), ntohs(address.sin_port), new_socket);
          if(con->login())
          {
            client_socket[i] = new_socket;
            m_clientConnectionSocketMap[new_socket] = con;
            LOG(INFO)<<"Client Connection Logon Complete";
          }
          else
            delete con;
          break;
        }
      }//for
    }
    else
    {
      try{
        for(int i = 0; i<m_max_clients; i++)
        {
          sd = client_socket[i];
          if(FD_ISSET(sd,&readfds))
          {
            if ( (valread = recv(sd, buffer, sizeof(buffer),MSG_DONTWAIT|MSG_NOSIGNAL)) <= 0 )
            {
             //remove from the fd listening set
              LOG(INFO)<<"RESET CLIENT_SOCKET:("<<sd<<")";
              client_socket[i]=0;
              handleDisconnect(sd,true);
           }
           else
           {
             std::map<int, ClientConnection*>::iterator client_connection_socket_iter = m_clientConnectionSocketMap.find(sd);
             if(client_connection_socket_iter != m_clientConnectionSocketMap.end())
             {
               client_connection_socket_iter->second->handle_message(buffer, valread);
               if(client_connection_socket_iter->second->m_logoff)
               {
                  LOG(INFO)<<"SOCKET LOGGED OFF:"<<sd;
                  client_socket[i]=0;
                  handleDisconnect(sd,true);
               }
             }
             else
             {
                LOG(ERROR)<<"UNABLE TO FIND SOCKET DESCRIPTOR:"<<sd;
             }
           }
          }
        }
      }catch(...)
      {
        LOG(ERROR)<<"EXCEPTION CATCH!!!";
      }
    }
  }

From the information given I would state the following: 根据给出的信息,我将陈述以下内容:

  • Do not use a thread for each connection. 不要为每个连接使用线程。 Since you're on Linux use EPOLL Edge Triggered Multiplexing. 由于您使用的是Linux,因此请使用EPOLL边缘触发多路复用。 Most newer web frameworks use this technology. 大多数较新的Web框架都使用此技术。 For more info check 10K Problem . 有关更多信息,请检查10K问题 By eliminating threads from the equation you're eliminating the possibilities of a deadlock and reducing the complexity of debugging / worrying about thread safe variables. 通过从方程式中消除线程,您消除了死锁的可能性,并降低了调试的复杂性/无需担心线程安全变量。
  • Ensure each connection when finished is completely closed. 确保完成连接后,每个连接均已完全关闭。
  • Ensure that you do not have some new firewall rules that popped up in iptables since the upgrade. 确保自升级以来,您没有在iptables中弹出的一些新防火墙规则。
  • Check any firewalls on the network to see if they are restricting certain types of activity (is your server on a new IP since the upgrade?) 检查网络上的所有防火墙,以查看它们是否限制了某些类型的活动(升级后服务器是否在新IP上?)

In short I would put my money on a thread deadlock and / or starvation. 简而言之,我会把钱浪费在线程僵局和/或饥饿上。 I've personally conducted experiments in which I've created a multithreaded server vs a single threaded server using Epoll. 我亲自进行了实验,其中我使用Epoll创建了多线程服务器与单线程服务器。 The results where night and day, Epoll blows away multithreaded implementation (for I/O) and makes the code simpler to write, debug and maintain. Epoll昼夜不停地运行,结果是浪费多线程实现(用于I / O),并使代码更易于编写,调试和维护。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM