Asynchrones prüfen ob (TCP) Ports offen sind - asyncio

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Marco82
User
Beiträge: 19
Registriert: Samstag 4. April 2020, 21:07

__deets__ hat geschrieben: Sonntag 5. April 2020, 18:16 Ich wuerde ja mal vermuten, dass im zweiten Fall gechachte Informationen geliefert werden. Dreh's mal um. Und natuerlich wie immer mehrfach ausfuehren.
Stimmt. Umgedreht ist meine Variante zwar immer noch schneller, aber nicht mehr ganz so deutlich.
Mehrfach ausgeführt hatte ich das sowieso immer.

Danke für den Tipp
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und dann musst du natuerlich auch noch wirklich asynchron parallel was machen. Momentan ist alles was du erreicht hast, den gleichen Code mit etwas mehr overhead seriell ablaufen zu lassen. JEDE Abfrage muss ihr EIGNER TASK sein, und dann wartest du bis die alle zuende sind.

Code: Alles auswählen

import sys
import asyncio
import socket
import time


TARGETS = [
    ('pymotw.com', 'https'),
    ('doughellmann.com', 'https'),
    ('python.org', 'https'),
    ('google.de', 'https'),
    ('stackoverflow.de', 'https'),
    ('beolingus.de', 'https'),
    ('leo.de', 'https'),
]


async def resolve_address(loop, address, protocol):
    return await loop.getaddrinfo(
        address, protocol,
        proto=socket.IPPROTO_TCP,
    )

event_loop = asyncio.get_event_loop()


async def main():
    res = await asyncio.gather(*(resolve_address(event_loop, *t) for t in TARGETS))
    print(res)


event_loop.run_until_complete(main())
Marco82
User
Beiträge: 19
Registriert: Samstag 4. April 2020, 21:07

So, ich fasse meinen Stand jetzt mal zusammen.

In ca. 20 Minuten habe ich folgende Threadpool Lösung aufgesetzt.

Code: Alles auswählen

from multiprocessing import Pool
import socket
import time

def check_port(port: int) -> bool:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(3)
    try:
        s.connect(('hostname', int(port)))
        s.shutdown(socket.SHUT_RDWR)
        return port, True
    except socket.timeout as ex:
        return port, False
    finally:
        s.close()

if __name__ == '__main__':
    start = time.perf_counter()
    p = Pool(5)
    print(p.map(check_port, [4839, 4840, 4841]))
    end = time.perf_counter()
    print('Duration: {}'.format(end - start))
Kurz und übersichtlich wie ich finde.
Mit Threading habe ich in Python übrigens genau so wenig Erfahrung wie mit asyncio.

Ich, als Gelegenheitsprogrammierer, werde asyncio zukünftig meiden.

Danke für die vielen meist sehr hilfreichen Tipps!
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das schöne an Typannotationen ist ja, dass sie noch so falsch sein können. Am besten komplett löschen und vergessen. Ist nur Festplattenplatzverschwendung.
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marco82: Das ist keine Lösung mit Threads sondern mit Prozessen. Ich würde für so etwas ja nicht `multiprocessing` nehmen sondern `concurrent.futures`. Da haben Thread- und Prozesspools die gleiche API und man kann sich leichter zwischen beiden entscheiden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ergänzend zu Sirius3 Anmerkungen zu Typannonationen: *Wenn* man die schon unbedingt benutzen will, dann sollte man sie auch auswerten. Wenn man sie nicht auswertet, machen sie keinen Sinn, oder noch schlimmer, verschlechtern den Code, weil sie Sachen behaupten die nicht stimmen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Sonntag 5. April 2020, 19:40 Das schöne an Typannotationen ist ja, dass sie noch so falsch sein können. Am besten komplett löschen und vergessen. Ist nur Festplattenplatzverschwendung.
Ja, bei so kleinen Spielzeuganwendungen ist es egal.

Falls man eine IDE verwendet, bekommt man fette Hinweise, wenn etwas mit den Typen nicht stimmt.
Unter anderem führt es auch dazu, dass die IDE besser vervollständigen kann.

Wer das nicht will, soll das nicht nutzen, aber anderen davon generell abzuraten mit so einer fadenscheinigen Begründung (Festplattenverschwendung) ist der falsche Weg.
Reaktion des Anfängers: "Der hat gesagt den Scheiß brauche ich nicht, also beschäftige ich mich damit nicht"


Übrigens kann asyncio nur langsamer als das sequentielle sein.
Getaddrinfo fragt den Resolver und der Resolver hat einen Cache.
Bei zweiten mal misst du nur noch den Cache und Geschwindigkeit des Interpreters.
Da der sequentielle Code signifikant weniger Funktionsaufrufe tätigt, läuft dieser auch schneller ab.

Würdest du stattdessen z.B. Verbindungen zu den Hosts aufbauen, wäre asyncio hier der klare Gewinner.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: dagegen sprechen mehrere Gründe: hier im konkreten Fall, dass die Annotation schlicht falsch ist. Das passiert relativ häufig, dass sie die Signaturen von Funktionen während der Entwicklung ändern, und dann noch jedesmal die Annotation nachgezogen werden muß (und dann vergessen wird). Zweitens sind die allermeisten Annotationen viel zu einschränkend. Dank Ducktyping läßt sich auch gar nicht die Gesamtheit aller möglichen Typen angeben. Sehr häufig schreibt man eine Funktion, die etwas berechnet, die man dann auch mit numpy-Arrays füttern kann, aber dank Annotation wird das als fetter Fehler angemerkt.
Drittens: nur weil ich weiß, dass eine Funktion ein Argument eines bestimmten Typs erwartet, weiß ich noch lange nicht, welche semantische Bedeutung das Argument hat. Umgekehrt, wenn ich weiß, was ein Argument bedeutet, dann ist in 99% der Fälle auch klar, von welchem Typ dieses Argument sein muß. Gerade Anfängern verleitet das dazu, sich auf den Typ zu verlassen und totalen Murks zu programmieren, der formal richtig aussieht.

Ich persönlich mache fast nie einen Programmierfehler, der nur darin lag, dass ich den falschen Typ übergeben habe und falls doch, fällt das beim ersten Test auf.
Zusammengefasst, die Vorteile sind marginal, die Fehler, die man dabei macht, größer und der zusätzliche Aufwand meiner Meinung nach nicht zu gerechtfertigen. Und dann muß man noch all die false-postive Fehler, die bei einer Prüfung auffallen auch noch abschlalten.

Die Arbeit ist besser in gute Doc-Strings investiert, die Deine Super-IDE hoffentlich auch parsen kann und zielgerichtetere Hilfe anbieten kann.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Dienstag 7. April 2020, 09:49 @DeaD_EyE: dagegen sprechen mehrere Gründe: hier im konkreten Fall, dass die Annotation schlicht falsch ist. Das passiert relativ häufig, dass sie die Signaturen von Funktionen während der Entwicklung ändern, und dann noch jedesmal die Annotation nachgezogen werden muß (und dann vergessen wird).
Wie ich bereits schreib, Spielzeuganwendungen kann man außer Acht lassen.
Ich schrieb auch, dass es der IDE hilft.
Sirius3 hat geschrieben: Dienstag 7. April 2020, 09:49 Zweitens sind die allermeisten Annotationen viel zu einschränkend. Dank Ducktyping läßt sich auch gar nicht die Gesamtheit aller möglichen Typen angeben. Sehr häufig schreibt man eine Funktion, die etwas berechnet, die man dann auch mit numpy-Arrays füttern kann, aber dank Annotation wird das als fetter Fehler angemerkt.
Wenn man es falsch macht, dann passiert das regelmäßig.

Code: Alles auswählen

MY_TYPE = Union[np.ndarray, typing.Sequence]
Sirius3 hat geschrieben: Dienstag 7. April 2020, 09:49 Drittens: nur weil ich weiß, dass eine Funktion ein Argument eines bestimmten Typs erwartet, weiß ich noch lange nicht, welche semantische Bedeutung das Argument hat. Umgekehrt, wenn ich weiß, was ein Argument bedeutet, dann ist in 99% der Fälle auch klar, von welchem Typ dieses Argument sein muß. Gerade Anfängern verleitet das dazu, sich auf den Typ zu verlassen und totalen Murks zu programmieren, der formal richtig aussieht.
Deswegen benutzt man auch Docstrings und Code, der sich selbst erklärt.
EDIT: Und Anfänger sollten sich lieber aufs programmieren konzentrieren.

Sirius3 hat geschrieben: Dienstag 7. April 2020, 09:49 Ich persönlich mache fast nie einen Programmierfehler, der nur darin lag, dass ich den falschen Typ übergeben habe und falls doch, fällt das beim ersten Test auf.
Zusammengefasst, die Vorteile sind marginal, die Fehler, die man dabei macht, größer und der zusätzliche Aufwand meiner Meinung nach nicht zu gerechtfertigen. Und dann muß man noch all die false-postive Fehler, die bei einer Prüfung auffallen auch noch abschlalten.
False Positives sind es nicht. Wenn du den Typen falsch oder nicht angibst, dann muss das auch angezeigt werden.
Entweder man setzt es richtig ein oder lässt es besser ganz bleiben (weil man sich sonst nur aufregt).
Sirius3 hat geschrieben: Dienstag 7. April 2020, 09:49 Die Arbeit ist besser in gute Doc-Strings investiert, die Deine Super-IDE hoffentlich auch parsen kann und zielgerichtetere Hilfe anbieten kann.
Seit wann sind Docstrings für den Computer?
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Seit wann sind Docstrings für den Computer?
Python kennt das doctest Modul. Und wenn ich mich nicht völlig irre gab es das schon, bevor es die ganzen Unittest Module gab.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DeaD_EyE: Wer sagt das Docstrings für den Computer sind? Die werden von einigen IDEs/Editoren dem *Benutzer* angezeigt wenn er das möchte. Beispielsweise als Popup wenn man sich mit dem Cursor in der Argumentenliste beim Aufruf befindet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

@noisefloor, ja kenne ich.
@blackjack: ja, die IDEs können ganz viel. Wer liest den Inhalt des Docstrings am Ende? Der Mensch vor dem Computer oder keine KI. Übrigens ist die Anzeige der Funktionen noch detaillierter, wenn man Typenannotationen verwendet. Setzt natürlich voraus, dass man sie richtig verwendet, sonst gibt es Konfusion.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DeaD_EyE: Ich weiss nicht was Du mir da sagen willst. Der Mensch liest den Inhalt des Docstrings am Ende. Und nicht der Computer. Der parst den Docstring aber. Also zumindest mal Pycharm macht das. Das ist in der Regel ja kein unstrukturierter Text sondern man kann da schon die Beschreibungen von einzelnen Argumenten raus fischen wenn das einer der Konventionen entspricht die beispielsweise Sphinx unterstützt, und nur die anzeigen wenn man mit dem Cursor über dem entsprechenden Argument ist, statt einfach immer alles was da steht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten