Hallo,
ich sehe mir gerade dieses Beispiel zum Erstellen von Server und Client
per TCP an:
http://www.c-worker.ch/tuts/wstut_op.php
Anstatt nach erfolgter Verbindung einen neuen thread zu starten, der
diesen bedient, möchte ich es bei nur einem einzigen belassen. Somit
kann auch nur ein Client mit meinem Server gleichzeitig verbunden sein.
Bei TCP ist die Verbindung fest geregelt. Bei UDP ist es aber so, dass
jeder den Server "anschreiben" kann. Wie ist es dann aber bei Antworten
VOM Server? Wird die Antwort dann an die IP geschickt, die die
Verbindung zuerst aufgenommen hat?
> [..] UDP [..] Wie ist es dann aber bei Antworten VOM Server? Wird die> Antwort dann an die IP geschickt, die die Verbindung zuerst aufgenommen> hat?
Nein, natürlich nicht. Die Antwort geht an die Adresse, die Du angibst.
brechbunkt schrieb:> Bei UDP ist es aber so, dass> jeder den Server "anschreiben" kann. Wie ist es dann aber bei Antworten> VOM Server? Wird die Antwort dann an die IP geschickt, die die> Verbindung zuerst aufgenommen hat?
Bei UDP gibt es keinen Zustand, den man "Verbindung" nennen könnte. Es
werden Pakete (bei UDP "Datagramm" genannt), versendet, die entweder
ankommen oder auch nicht. Da steht die Quelladresse drinnen. Wenn der
Empfänger mag, kann er diese auslesen und eine Antwort senden ...
Danke für die Antworten.
Frank Esselbach schrieb:> Es werden Pakete versendet, die werden entweder ankommen> oder auch nicht. Da steht die Quelladresse drinnen.> Wenn der Empfänger mag, kann er diese auslesen und> eine Antwort senden ...
Heißt das dann also, der erste Client der gesendet hat (somit seine
Quelladresse bekannt ist) und dessen Nachricht der Server als erstes
entgegen genommen hat, der bekommt dann immer die Antworten vom Server
zurück?
(In meinem Fall kann nur ein Client bedient werden, da nur ein thread)
Ich habe das oben genannte Beispiel nun mit UDP umgesetzt. Während der
Programmierung hatte ich immer per loopback getestet. Da war es
verständlich, dass ich immer das richtig sende und empfange. Nun habe
ich die Verbindung von meinem Linux-Rechnter aus ("nc [ip] [port]") am
Windows-Server eröffnet und ohne am Windows die IP zu nennen kam die
Antwort richtig zum Linux zurück. Zufall oder normal?
brechbunkt schrieb:> Heißt das dann also, der erste Client der gesendet hat (somit seine> Quelladresse bekannt ist) und dessen Nachricht der Server als erstes> entgegen genommen hat, der bekommt dann immer die Antworten vom Server> zurück?
Nein. <seufz>
Wenn dein Server den UDP-Socket erzeugt hat, dann hast du im folgenden
zwei Möglichkeiten:
1. du rufst anschließend auf dem Socket connect() auf. Einer der
Parameter von connect() ist die Zieladresse. Anschließend kannst du mit
send() über den Socket Daten verschicken, die an diese Adresse gehen.
2. du rufst connect() nicht auf. Dann darfst du im folgenden auch nicht
send() verwenden. Aber du kannst mit sendto() auf den Socket schreiben,
wobei einer der Parameter für sendto() die Zieladresse ist.
sendto() ist übrigens auch im ersten Fall erlaubt. Damit kannst du
Pakete "außer der Reihe" an eine andere Adresse schicken als die die du
vorher mit connect() festgelegt hast.
Wie du das logisch handelst, bleibt vollkommen dir überlassen. Eine
mögliche (und logische) Variante wäre, daß dein Server mit recvfrom()
auf ein eingehendes Paket wartet.
Sobald eins kommt, hat er die Absendeadresse (die wird von recvfrom()
zurückgegeben) und kann dann mit sendto() seine Antwort schicken.
Anschließend wieder zurück zur Warteschleife.
Alles obige gilt so zumindest für die Linux-Implementierung der
Berkeley-Sockets. Wie kompatibel WinSock ist, weiß ich nicht.
Interessiert mich auch nicht. Windows ist keine Zielplattform für mich.
Noch ein Vorteil von Linux: Dokumentation. Das obige steht alles so in
der Manpage zu udp(7)
Stell Dir UDP wie eine Postkarte vor. Jemand schickt eine Nachricht
irgendwo hin und hofft, dass sie auch ankommt.
Es gibt keine formelle Antwort. Es gibt keine Verbindung.
Ich schicke Dir "Hallo, wie geht es Dir".
Du schickst mir "Mir geht es gut", falls du dazu Lust hast und meine
Frage erhalsten hast.
Deine Postkarte unterscheidet sich technisch nicht von meiner. Der
Postbote kann nicht erkennen, welche der Karten die Frage und welche die
Antwort war, es sei denn, er liest die Texte und interpretiert sie
intelligent.
UDP ist aber weder intelligent, noch schaut es in die Nutzdaten hinein.
Für UDP ist jede Nachricht einfach nur ein Datenpaket, dass von irgend
jemandem irgend wohin gesendet wird.
Nichtmal die Port-Nummern haben für das Protokoll irgendeine Bedeutung.
Jedes Paket hat
Sender-Adresse + Sender-Port
sowie
Empfänger-Adresse + Empfänger-Port
Die Port Nummern sind für das Netzwerkprotokoll jedoch völlig
bedeutungslos.
Manche Programme senden etwas an Port X und erwarten die Antwort
ebebnfalls auf Port X. Andere Programme hingegen handhaben das ganz
anders, zum Beispiel DHCP.
brechbunkt schrieb:> Bei UDP ist es aber so, dass> jeder den Server "anschreiben" kann.
Ja, es gibt keine Verbindung.
> Wie ist es dann aber bei Antworten> VOM Server?
Wo es keine Verbindung gibt, gibt es auch keine Antworten, sondern nur
Fragen. Zu einem Frage-Antwortspiel kann es erst werden, wenn deine
Programmlogik eines daraus macht, also selber eine logische Verbindung
zwischen Fragen und Antworten schafft.
> Wird die Antwort dann an die IP geschickt, die die> Verbindung zuerst aufgenommen hat?
Die Antwort wird an die Adresse geschickt, die du angibst. Wenn das die
ist, von der die zugehörige Frage kam, hast du in erster Näherung eine
Art Verbindung geschaffen. Aber: ein zweiter Kommunikationspartner auf
demselben Host bringt schon das Gebäude zum Einsturz.
Um auf UDP sowas wie ein Verbindung aufzusetzen, genügt es also nicht,
eine IP-Adresse als Verbindungmerkmal zu speichern. Zusätzlich muß der
Port gespeichert werden.
Das Problem ist nun, daß du den auf dem "Server" garnicht weisst, du
kennst nur den Port, von dem die Frage gesendet wurde, das ist nicht
notwendigerweise der Port, auf dem der Peer die Antwort erwartet.
Deine Programmlogik muß also sowohl auf dem "Client" als auch auf dem
"Server" eine (übereinstimmende) Logik enthalten, nach der bestimmt
wird, auf welchem Port der Peer die Antwort auf die Frage erwarten wird.
Im Übrigen gibt es bei UDP mit logischer Verbindung keinen "Client" oder
"Server". Beide Peers sind eigentlich immer beides.
Zum dedizierten Client oder Server werden sie auch erst wieder durch
deine Programmlogik, die sich dementsprechend zwischen Client und Server
unterscheiden muß. Einer muß auf Fragen warten und nur reden, wenn er
welche gestellt bekommt, das ist dann der Server. Der Client hingegen
muß aktiv werden, er muß die Fragen stellen und damit die Kommunikation
anstoßen.
Tipp am Rande: Mancher zieht daraus den Schluss, dass UDP nur für
Botschaften sinnvoll ist, die keine hin-und-her Kommunikation
darstellen. Und kann ziemlich daneben liegen. Ich hatte schon Beispiele
von Client/Server-Anwendungen, die mit TCP völlig auf die Nase fielen
und UDP den weit sinnvolleren Weg dargestellt hätte. Nicht immer ist die
Transportabsicherung von TCP hilfreich, insbesondere wenn auf anderen
Layern der Kommunikation ebenfalls schon eine solche stattfindet.
Stefan us schrieb:> Nichtmal die Port-Nummern haben für das Protokoll irgendeine Bedeutung.
Das stimmt so nicht. Die Portnummer ist Teil der Adresse. Ohne
Portnummern könnte UDP nur genau einen Empfänger pro Computer haben und
wäre recht nutzlos. Erst dadurch daß die Zieladresse eines UDP-Pakets
eine Portnummer enthält, kann der IP-Netzwerkstack entscheiden, in
welchen Socket er das Paket übergibt.
Als Analogie nehme man die Hausnummer in einer Postadresse. Ohne
Hausnummer kommt die Postkarte zwar in die richtige Stadt und in die
richtige Straße. Aber erst mit einer Hausnummer kann der Postbote
entscheiden in welchen Briefkasten er sie stecken muß (in diesem
vereinfachten Modell gibt es an jedem Haus nur einen Briefkasten).
Nur am Rande:
brechbunkt schrieb:> Anstatt nach erfolgter Verbindung einen neuen thread zu starten, der> diesen bedient, möchte ich es bei nur einem einzigen belassen. Somit> kann auch nur ein Client mit meinem Server gleichzeitig verbunden sein.
Du kannst auch in einem Thread beliebig viele Verbindungen haben. Mit
select() oder poll() kannst du auf Daten von mehreren gleichzeitig
warten. Damit kommt man ziemlich weit, ohne sich mit Threads rumschlagen
zu müssen. Die braucht man eigentlich erst, wenn man soviel Last hat,
dass man sie auf mehrere Prozessorkerne verteilen muss oder wenn das
Generieren einer Nachricht so viel Zeit brauchen kann, dass andere
Verbindungen dann sehr lange blockiert wären.
Ja stimmt. Nur bestimtm die Anwendung selbst, auf welchen Ports sie
lauschen möchte. Das Protokoll schreibt nicht vor, dass die Port Nummer
einer Antwort mit der Portnummer einer Anfrage identisch sein muss.
Rolf Magnus schrieb:> brechbunkt schrieb:>> Anstatt nach erfolgter Verbindung einen neuen thread zu starten, der>> diesen bedient, möchte ich es bei nur einem einzigen belassen. Somit>> kann auch nur ein Client mit meinem Server gleichzeitig verbunden sein.>> Du kannst auch in einem Thread beliebig viele Verbindungen haben. Mit> select() oder poll() kannst du auf Daten von mehreren gleichzeitig> warten.
Nur bringt das vergleichsweise wenig, wenn man einen UDP-Server baut.
Denn der hat ja i.d.R. nur genau einen Socket. Im Gegensatz zu TCP, wo
man den listen()ing Socket hat, auf dem man accept() macht, das dann für
jede neue Verbindung einen neuen Socket ausspuckt.
Bei UDP hat man hingegen nur den einen Socket. Und aus dem kann man
Pakete auch nur sequentiell auslesen. So lange das Programm regelmäßig
zum revcfrom() kommt, ist alles in Butter. Darüber hinaus hat der
IP-Stack ja auch noch eine Warteschlange (queue) für eingehende Pakete.
Axel Schwenke schrieb:> Bei UDP hat man hingegen nur den einen Socket.
Nicht zwangsläufig. Man kann über diesen einen Socket einen separaten
Port für die Kommunikation aushandeln. Dann geht auch mehr parallel.