Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:实现并发DNS请求(流水线)_Python_Sockets_Dns_Task_Python Asyncio - Fatal编程技术网

Python:实现并发DNS请求(流水线)

Python:实现并发DNS请求(流水线),python,sockets,dns,task,python-asyncio,Python,Sockets,Dns,Task,Python Asyncio,我正在尝试编写一个python脚本来发送多个DNS请求,每个请求使用不同的名称服务器 使用dnspython实现顺序解决方案很容易,但对我来说太慢了。 使用线程池添加并发是不可能的,因为在我的特殊情况下,所有请求都使用相同的源端口重用地址在这里也没有帮助 出于上述原因,我考虑使用以下解决方案放弃使用dnspython的解析器模块,但利用其消息构建和解析模块: 最多允许X个请求正在进行 同时发送X个请求,仅使用udp发送dns请求数据包。可能在发送之间增加延迟以避免突发 另一个线程等待响应 当响应

我正在尝试编写一个python脚本来发送多个DNS请求,每个请求使用不同的名称服务器

使用dnspython实现顺序解决方案很容易,但对我来说太慢了。 使用线程池添加并发是不可能的,因为在我的特殊情况下,所有请求都使用相同的源端口重用地址在这里也没有帮助

出于上述原因,我考虑使用以下解决方案放弃使用dnspython的解析器模块,但利用其消息构建和解析模块:

最多允许X个请求正在进行 同时发送X个请求,仅使用udp发送dns请求数据包。可能在发送之间增加延迟以避免突发 另一个线程等待响应 当响应到达时,按地址将其与请求匹配,并允许运行新请求 如果请求响应未在超时秒内到达,则将其标记为已完成,并允许运行新请求 我的主要问题是:

如何轻松实现任务超时 是否可以在不使用线程同步(例如使用事件循环)的情况下实现它? 有没有任何现有的库可以帮助实现它?我真的觉得我在尝试重新发明轮子。我查看了asycnio模块,但找不到一种方法来利用它解决我的问题。请注意,我不想使用现有的dns或网络库,因为我需要更改核心功能的灵活性,例如使用原始套接字、更改dns标头字段等。。 你试过aiodns软件包吗


对于超时,asyncio有标准的等待协程。

使用简单的选择循环在这里效果很好。以下是要完成的代码片段:

def run(self, resolvers_iter):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
    sock.setblocking(False)

    try:
        pending = []

        # For rate limiting
        limit = float(self.timeout)/self.max_pending  # delay between sends
        last_sent = clock() - limit

        # Work as long as there are more resolvers to query
        completed_sending = False
        while not completed_sending or pending:

            # Can I send more requests
            want_to_write = False
            if not completed_sending and len(pending) < self.max_pending:
                want_to_write = True

            # Calculate nearest timeout time to make sure select returns on time
            timeout = None
            if pending:
                timeout = self.timeout - clock() + pending[0][0] + 0.001
                timeout = max(timeout, 0)

            # Rate limit
            time_passed_since_send = clock() - last_sent
            if want_to_write and time_passed_since_send + 0.001 < limit:
                timeout = min(timeout, limit-time_passed_since_send)
                timeout = max(timeout, 0)
                want_to_write = False

            # Poll socket - uses internally the select module
            readable, writable = self._select(readable=True, writable=want_to_write, timeout=timeout)

            # Can read
            if readable:
                # Read as many as possible
                while True:
                    try:
                        # Get response
                        response, from_address = DnsFacilities.read_response(sock)

                        # Check if not duplicate or already timed out
                        sent_time = None
                        for i, (t, ip) in enumerate(pending):
                            if ip == from_address[0]:
                                sent_time = t
                                del pending[i]
                                break

                        if sent_time is not None:
                            self.response_received((response, from_address, clock()-sent_time))

                    except socket.error, e:
                        if e[0] in (socket.errno.EWOULDBLOCK, socket.errno.EAGAIN):
                            break
                        elif e[0] in (socket.errno.WSAECONNRESET, socket.errno.WSAENETRESET):
                            pass
                        else:
                            raise

            # Can write
            if writable:
                try:
                    last_sent = clock()
                    resolver_address = resolvers_iter.next()
                    DnsFacilities.send_query(resolver_address)
                    pending.append((clock(), resolver_address)
                except StopIteration:
                    completed_sending = True

            # Check for timed out tasks
            now = clock()
            while pending and now - pending[0][0] > self.timeout:
                self.response_timeout(pending[0][1])
                del pending[0]

    finally:
        sock.close()

我需要为每个请求使用不同的名称服务器,而aiodns似乎不是为此而设计的。您可以为每个名称服务器创建DNSResolver实例,并通过nameservers参数指定所需的DNS ip。DNSResolver的实例化是相对轻量级的任务。