【发布时间】:2017-02-22 07:15:14
【问题描述】:
我最近获得了多个项目的想法,这些想法都涉及从文件中读取 IP 地址。由于它们都应该能够处理大量主机,因此我尝试实现多线程或创建套接字池并从中选择(),以实现某种形式的并发以获得更好的性能.在很多情况下,从文件中读取似乎是提高性能的瓶颈。我理解它的方式,从具有 fgets 或类似功能的文件中读取是一种同步的阻塞操作。所以即使我成功实现了一个异步连接到多个主机的客户端,操作仍然是同步的,因为我一次只能从文件中读取一个地址。
/* partially pseudo code */
/* getaddrinfo() stuff here */
while(fgets(ip, sizeof(ip), file) {
FD_ZERO(&readfds);
/* create n sockets here in a for loop */
for (i = 0; i < socket_num; i++) {
if (newfd > fd[i]) newfd = fd[i];
FD_SET(fd[i], &readfds);
}
/* here's where I think I should connect n sockets to n addresses from file
* but I'm only getting one IP at a time from file, so I'm not sure how to connect to
* n addresses at once with fgets
*/
for (j = 0; j < socket_num; j++) {
if ((connect(socket, ai->ai_addr, ai->ai_addrlen)) == -1)
// error
else {
freeaddrinfo(ai);
FD_SET(socket, &master);
fdmax = socket;
if (select(socket+1, &master, NULL, NULL, &tv) == -1);
// error
if ((recvd = read(socket, banner, RECVD)) <= 0)
// error
if (FD_ISSET(socket, &master))
// print success
}
/* clear sets and close sockets and stuff */
}
我已经指出了我与 cmets 的问题,但只是为了澄清一下:我不确定如何在从文件读取的多个目标服务器上执行异步 I/O 操作,因为从文件中读取条目似乎是严格同步的.我在多线程方面遇到过类似的问题,但成功程度略高。
void *function_passed_to_pthread_create(void *opts)
{
while(fgets(ip_addr, sizeof(ip_addr), opts->file) {
/* speak to ip_addr and get response */
}
}
main()
{
/* necessary stuff */
for (i = 0; i < thread_num; i++) {
pthread_create(&tasks, NULL, above_function, opts)
}
for (j = 0; j < thread_num; j++)
/* join threads */
return 0;
}
这似乎可行,但由于多个线程都在处理同一个文件,因此结果并不总是准确的。我想这是因为多个线程可能同时处理来自文件的相同地址。
我考虑过将文件中的所有条目加载到数组/内存中,但如果文件特别大,我想这可能会导致内存问题。最重要的是,我不确定这样做是否有意义。
作为最后一点;如果我正在读取的文件恰好是具有大量 IP 的特别大的文件,那么我认为这两种解决方案都不能很好地扩展。不过,使用 C 一切皆有可能,所以我想有一些方法可以实现我希望的目标。
总结这篇文章;在从文件中读取条目时,我想找到一种方法来使用异步 I/O 或多线程来提高客户端应用程序的性能。
【问题讨论】:
-
我感受到了你的痛苦,但是有很多方法可以读取文件。我知道,最快的方法是使用本机 API 函数(即 ReadFile)顺序读取文件。然后您可以将处理分拆到不同的线程等。
-
这对于读取具有多个线程读取this 的文件是没有用的。你应该有一个经理来阅读文件并给每个线程一行文件。
-
在典型的 Linux 系统上,打开描述符(文件和套接字)的最大数量的硬限制通常为 65536。(软限制,即默认值,通常要低得多,例如 1024。 ) 即使假设使用了极长的主机名和服务名(用于连接,而不是端口号),每个客户端通常仍需要少于 100 个字符。 65536×100 只有大约 6.5 兆字节。读取和标记它(到主机和端口字符串对)只需要几分之一秒。 这无关紧要。
-
假设您只需要 IPv6(或 IPv4)地址。
struct sockaddr_storage通常为 128 字节。因此,如果您从文件中读取每个主机和端口对,并使用线程池将它们解析为使用getnameinfo()的可连接套接字(但立即关闭每个连接,以便稍后进行实际连接),则每个需要大约 144 个字节客户。那时即使是一百万个客户端也只有 144 兆字节;如果限制为 IPv4,则只有 24 兆字节。除非您以数字格式存储主机名,否则名称解析将花费最多的时间! -
如果您决定以数字格式存储主机名,那么您可以切换到二进制格式;一个文件用于
struct sockaddr_ins,另一个用于struct sockaddr_in6s。您可以以闪电般的速度阅读它们,并编写单独的实用程序将它们转换为文本文件/从文本文件转换,并具有可选的名称解析。所以,总的来说,我只是不明白你在哪里发现问题;我在这里没有看到。 (我自己也处理大型数据集,所以我在这里的逻辑中没有假设任何大小限制。)
标签: c multithreading pthreads