【问题标题】:Creating multiple TCP Socket connections创建多个 TCP Socket 连接
【发布时间】:2012-09-18 06:51:45
【问题描述】:

在我的一个解析程序中,我必须执行以下操作(在 C、C++、VC++ 中):-

我必须通过套接字编程连接 100 台机器(透析机)。 向每台机器发送请求并接收来自机器的响应以及我将从机器获得的数据我必须解析该数据并将值写入文件中。我必须每 5 秒后向每台机器发送请求.

所以要完成上述任务,我想这样做:-

我将从数据库中读取每台机器的ip和端口,创建一个线程连接到每台机器,并在每个线程中创建一个子线程(子线程),它将发送和接收并解析来自的数据每 5 秒后机器(并将值写入 txt 文件)。 我的解析功能会通用。

这是可行的解决方案。 请帮助我。提前谢谢。

【问题讨论】:

  • 对于数据的写入,我建议您将所有信息发送回 1 个线程并从那里写入。
  • 线程不是正确的解决方案(尤其是 100 个线程)。使用一个线程发出请求。当您收到回复时,将其传递给工作组进行处理(工作组应该有大约 <cpu count> * 1.5 线程(当然要测量以找到正确的配给))
  • @LokiAstari - 100 个线程不算什么。它们在 5 秒的大部分时间里都被阻止,所以这不是问题。
  • @Minion91 - 我不会那样做,而不是 100 个独立连接。一台无法访问的机器会在其余 99 台机器上造成网络超时。
  • @MartinJames:不仅是使用 CPU 的线程,还有与线程一起使用的其他资源(例如堆栈的内存)。尤其是当它可以在单个线程中完成所有事情时(这也使代码更易于编写和阅读)。我们使用三个线程来维持 1500 个同时连接。一个线程发出请求并等待响应。一个线程下载数据,一个线程维护计划并平衡对站点的请求,以确保我们是always polite

标签: c++ c multithreading sockets visual-c++


【解决方案1】:

除了一点之外,您的解决方案听起来很合理。您提到您将创建一个线程来连接到每台机器,然后创建一个子线程来管理发送、接收和解析。我不明白你为什么需要创建一个子线程。您应该能够处理连接线程中的所有内容。还要考虑每个连接 1 个线程可能无法很好地扩展,如果此应用程序必须处理大量机器,则应避免每台机器一个线程。

甚至可以通过一个简单的线程池而不是每个连接 1 个线程来实现这一点,这不会很好地扩展。您可以考虑创建每 5 秒放入工作队列的任务,线程池将连接、读取、断开连接、解析和处理。假设这是 TCP/IP,您可能不应该保持连接打开,而是每次读取都连接/断开连接,类似于 HTTP。

Here 是一个 vc++ 线程池相关的问题。而here 是一些更相关的信息。

另一种选择是使用libevent 进行套接字通信。至于解析,还有Apache Thrift或JSon等其他库可以使用,都是开源的。这些解析库的缺点是您可能还必须修改透析机,这可能不是一个选项。如果您可以使用 Thrift 之类的工具,则可以从一个库中获取所有内容:套接字通信和解析。

以下是每个连接 1 个线程的简单情况的一些代码:

class ThreadInfo
{
public:
  ThreadInfo(const string &ipAddress, uint16_t port) : ipAddress_(ipAddress), port_(port) {}
  string getIpAddress() {return ipAddress_;}
  uint16_t getPort() {return port_;}
  string getRecvBuffer() {return recvBuffer_;}

private:
  string ipAddress_;
  uint16_t port_;
  string recvBuffer_;
};

void *threadEntryPoint(void *userData)
{
  ThreadInfo *threadInfo = (ThreadInfo*) userData;

  // You need to decide if you want to keep the connection open while sleeping
  // or open and close it for each transaction. Change code here accordingly.
  // Create socket with threadInfo->getIpAddress() and threadInfo->getPort()

  // while(1)
  //   Send request to each machine
  //   Get response from each machine and store in threadInfo->getRecvBuffer()
  //       The buffer could also be a local var in this function, decide accordingly
  //   parse data accordingly
  //   sleep 5 seconds
}

uint16_t getPort(int machineNum) { return 3456; }
string getIpAddress(int machineNum) { return string("192.168.1.2"); }
int main(int argc, char **argv)
{
   // 3 items that we need, and that you will have to plugin accordingly:
   //   1) Num threads, assuming 100 for now
   //   2) IP address of each external machine, implement getIpAddress() accordingly
   //   3) port of each machine, implement getPort() accordingly

   int numThreads(100);
   list<pthread_t> threadIdList;

   for(int i = 0; i < numThreads; ++i)
   {
      pthread_t threadId;
      ThreadInfo *threadInfo = new ThreadInfo(getIpAddress(i), getPort(i));
      pthread_create(&threadId, NULL, threadEntryPoint, threadInfo);
      threadIdList.push_back(threadId);
   }

   // Wait for the threads to finish
   std::list<pthread_t>::iterator iter = threadIdList.begin();
   while(iter != threadIdList.end())
   {
     pthread_t threadId = *iter++;
     pthread_join(threadId, NULL);
   }
}

【讨论】:

  • @Dany,对不起,我开始但被打断了。我会在 5 分钟内更新答案。
  • @Dany,我添加了一些代码。我还没有编译它,所以你必须测试它。
  • 感谢我将运行它的代码,如果需要与代码相关的问题,我可以进一步询问吗?
  • Siu没有把buffer传给线程,传最大的问题就是buffer,怎么做100个buffer传给每个线程
  • @Dany,我不确定您指的是哪个缓冲区:读取缓冲区或发送到机器的命令。无论哪种方式,这些缓冲区可以/应该在 ThreadInfo 对象中。如果它是要发送到机器的命令缓冲区,则使用命令实例化 ThreadInfo 对象。
【解决方案2】:

对于 100 台机器,每 5 秒轮询一次,每台机器一个线程是合理的 - 线程将在大部分时间在 Sleep(5000) 上阻塞,并在套接字 I/O 或磁盘 I/O 上阻塞其余大部分时间。对于这种加载(甚至是加载的 5 倍),我认为没有必要诉诸异步 I/O 或线程池 - 为什么不必要地使事情复杂化?

正如@Brady 所指出的,我不明白为什么每个连接需要多个线程,假设您发布的要求是 - 只需每约 5 秒轮询一次并将回复写入文本文件。

我猜,(希望:),5 秒的时间间隔不是安全关键的实时要求,如果由于某些临时软件,该时间间隔偶尔为 6 秒,透析机将继续正常运行或网络延迟。我不是血液科医生/肾脏科医生,但如果任何透析机可以对整体治疗产生任何重大改变(这需要数小时),如果投票/指示偶尔延迟一秒钟,我会感到惊讶。

编辑 - 重新。 '解析函数并将数据写入文件函数对于所有线程都是通用的' - 应该没问题,假设每台机器都有不同的文本文件。如果日志全部写入一个日志文件,那就更成问题了——每个日志条目实际上应该排到一个记录器线程的队列中,该线程单独写入日志文件。使用已经支持这种功能的现有的、经过验证的记录器框架将是最简单的解决方案。

【讨论】:

【解决方案3】:

如果您对网络和磁盘 IO 都使用 boost::asio 框架,您可能会得到比每台机器处理一个线程少得多的线程。

另外,它有一个很好的高级套接字编程接口。

【讨论】:

    猜你喜欢
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多