Mal ein paar Fragen zu UDP (inzwischen auch TCP)

Übersicht BlitzBasic Allgemein

Gehe zu Seite Zurück  1, 2, 3  Weiter

Neue Antwort erstellen

 

Nox

BeitragMo, Sep 19, 2005 17:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Gibt es die MAV denn nur, wenn Clients zum Server verbunden sind, und dieser dann schließt? Sollte dies der Fall sein, schließe doch einfach gegen Ende alle Verbindungen mit den Clients und erst dann den Socket, auf dem dein Server auf Verbindungen wartet.

Wenn ich was verwechselt habe, ignorieren. Smile

Jolinah

BeitragMo, Sep 19, 2005 17:19
Antworten mit Zitat
Benutzer-Profile anzeigen
Hast du Debug an? Ich denke da sollte eine etwas genauere Information kommen. Vermutlich ein Stream does not exist oder so.

Hab hier mal ein Beispiel geschrieben, damit funktionierts bei mir zumindest. Mann kann auch Streamprobleme mit Eof Abfangen damit das Programm nicht abstürzt wenn ein Client das Fenster schliesst statt sich richtig abzumelden:

Server:
Code: [AUSKLAPPEN]
;Type für die Clients
Type client
   Field stream
   Field name$
End Type

;Server erstellen
tcp = CreateTCPServer(1010)

If tcp = 0 Then
   Print "Server starten fehlgeschlagen."
   End
Else
   Print "Server gestartet auf Port 1010."
EndIf


;Hauptschleife
While Not KeyHit(1)
   
   ;Client-Streams annehmen
   stream = AcceptTCPStream(tcp)
   If stream
      c.client = New client
      c\stream = stream
      c\name$ = ReadString(c\stream)
      Print "Client " + c\name$ + " eingeloggt."
   EndIf   

   ;Jeden Client durchgehen...
   For c.client = Each client

      ;Wenn Eof -1 oder 1 ist kann nichts mehr mit dem Stream gemacht werden
      ef = Eof(c\stream)
      If ef = -1 Then
         Print "Verbindungsproblem mit Client: " + c\name$
         WaitKey()
         CloseTCPStream(c\stream)
         Delete c
      ElseIf ef = 1
         Print c\name$ + " hat den Stream geschlossen ohne sich abzumelden."
         WaitKey()
         CloseTCPStream(c\stream)
         Delete c
      
      ;Wenn kein Stream-Fehler wird geschaut ob Daten da sind...
      ElseIf ReadAvail(c\stream)
         msg$ = ReadString(c\stream)
         
         pos = Instr(msg$, " ")
         
         If pos > 0
            cmd$ = Left(msg$, pos-1)
         Else
            cmd$ = msg$
         EndIf
         
         Select cmd$
            ;Wird Logout empfangen so schliesst man den Stream und löscht den Client
            Case "LOGOUT"
               Print "Client " + c\name$ + " ausgeloggt."
               CloseTCPStream(c\stream)
               Delete c

            ;Sender der Client ein Print gibt man den Text aus..
            Case "PRINT"
               Print "Client " + c\name$ + " sendet: " + Mid(msg$, pos+1)

         End Select

      EndIf
   Next

Wend

;Falls noch clients da sind Streams schliessen
For c.client = Each client
   ef = Eof(c\stream)

   ;Nur schreiben wenn kein Stream-Fehler
   If ef = 0
      WriteString(c\stream, "KICK")
   EndIf
   
   CloseTCPStream(c\stream)
   Delete c
Next

;Server beenden
CloseTCPServer(tcp)

End


Client
Code: [AUSKLAPPEN]
;TCP Verindung öffnen
tcp = OpenTCPStream("localhost", 1010)

If tcp = 0 Then
   Print "Verbindung mit dem Server fehlgeschlagen."
   WaitKey()
   End
Else
   Print "Eine Verbindung mit dem Server wurde hergestellt."
EndIf

;Clientname
name$ = "Test-Client"
WriteString(tcp, name$)


;Variablen zum Beenden
quit = False
send_quitmsg = True      ;gibt an ob die Logout Message beim Beenden an den Server geschickt wird.


;Hauptschleife
While (Not KeyHit(1)) And (Not quit)
   
   ;Auf Stream-Fehler prüfen..
   ef = Eof(tcp)
   If ef = -1
      Print "Verbindungsproblem. Ende."
      WaitKey()
      send_quitmsg = False
      quit = True

   ElseIf ef = 1
      Print "Verbindungsproblem. Ende"
      WaitKey()
      send_quitmsg = False
      quit = True

   ;Kein Fehler, schauen ob Daten verfügbar sind..
   ElseIf ReadAvail(tcp)
      msg$ = ReadString(tcp)

      Select msg$
         ;Bei KICK Meldung vom Server beenden wir den Client.
         Case "KICK"
            Print "Der Server wurde heruntergefahren."
            WaitKey()
            ;Bei einem kick braucht der Client nicht mehr Bye zu sagen (send_quitmsg)
            send_quitmsg = False
            quit = True

      End Select

   EndIf

   ;Leertaste sendet ein Kommando an den Server.
   If KeyHit(57) Then
      WriteString(tcp, "PRINT Hallo Welt!")
   EndIf

Wend


;Quitmessage senden
If send_quitmsg Then
   ef = Eof(tcp)
   
   ;Nur wenn kein Verbindungsfehler vorhanden..
   If ef = 0
      WriteString(tcp, "LOGOUT")
   EndIf
EndIf

;Stream schliessen
CloseTCPStream(tcp)

End


Hoffe das hilft dir weiter Wink

Smokie

BeitragMo, Sep 19, 2005 18:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe jetzt mal ne dumme Frage:
Ich habe zwar gelsen, dass Dim Felder für UDP etc. besser sind, weil die schneller sind, aber ich komme mit Types besser zurecht. Würde es sich also lohnen das Projekt mit Internet mit Types zu machen, die sind ja langsamer. Und wenn das oaky wäre bis zu welcher Menge an Daten kann man Types benutzen ohne das es "ruckelt".

Danke

Smokie
M-Soft Studios Mit selbst gemachtem Forum!!!
Unbequem lebt's sich schwer.
Musik macht erst Spaß, wenn man sie mit 2,8 facher Geschwindigkeit hört
Zuletzt bearbeitet von Smokie am Fr Jan 01, 1888 51:58 PM, insgesamt 1000-mal bearbeitet
 

BIG BUG

BeitragMo, Sep 19, 2005 22:14
Antworten mit Zitat
Benutzer-Profile anzeigen
@Mau
Nox hat recht, Du musst erst alle Clientstreams mit CloseTCPStream schließen(auch wenn diese bereits Clientseitig abgebrochen wurden), erst dann kannst Du CloseTCPServer verwenden. So gibt es zumindest bei mir keine Probleme

@Smokie
Keine Ahnung wo Du das gehört hast, jedenfalls ist es Unsinn. Mit einem Typesystem dürfte das Realisieren der Netzwerkunterstützung sogar etwas leichter ausfallen.
B3D-Exporter für Cinema4D!(V1.4)
MD2-Exporter für Cinema4D!(final)
 

Mau

BeitragDi, Sep 20, 2005 15:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
@Mau
Nox hat recht, Du musst erst alle Clientstreams mit CloseTCPStream schließen(auch wenn diese bereits Clientseitig abgebrochen wurden), erst dann kannst Du CloseTCPServer verwenden. So gibt es zumindest bei mir keine Probleme


Danke Leute - CloseTCPServer scheint jetzt zu klappen. Ihr hattet recht. Der Host muß zuerst den Stream zu jedem Client kappen, bevor der Server geschlossen wird. Ausserdem muß jeder Client den Strom von seiner Seite aus auch mit CloseTCPStream kappen. Nur so funktioniert das bei mir (endlich mal!).

@smokie: Das ist Quatsch. Types sind nur minimal langsamer - jedenfalls so gering, daß sich ein Unterschied höchstens in einer Schleife auswirkt, die man zigtausendmal pro Frame durchläuft. Den Unterschied kannst du in 99% aller Fälle getrost ignorieren. Ist schlicht nicht bemerkbar. Ich denke, solche Sachen bringen immer wieder sog. "Old School"-Neandertaler auf, die sich einfach nicht umgewöhnen wollen (oder können). Meine Meinung Wink
Alles muss, nichts kann!

Smokie

BeitragDi, Sep 20, 2005 16:24
Antworten mit Zitat
Benutzer-Profile anzeigen
Juhu wenn das so ist versuche ich auch mal ein kleines Chat-programm zu schreiben. Ich hatt nähmlich immer so ein rpoblem mit der verwaltung von den Dims
Danke
Grüße
Smokie
M-Soft Studios Mit selbst gemachtem Forum!!!
Unbequem lebt's sich schwer.
Musik macht erst Spaß, wenn man sie mit 2,8 facher Geschwindigkeit hört
Zuletzt bearbeitet von Smokie am Fr Jan 01, 1888 51:58 PM, insgesamt 1000-mal bearbeitet
 

Nox

BeitragMi, Sep 21, 2005 16:02
Antworten mit Zitat
Benutzer-Profile anzeigen
Mau hat Folgendes geschrieben:
Danke Leute - CloseTCPServer scheint jetzt zu klappen. Ihr hattet recht. Der Host muß zuerst den Stream zu jedem Client kappen, bevor der Server geschlossen wird.


Vielleicht sollte man noch kurz anmerken, wieso das so ist: Über Blitz wird's nicht ganz deutlich, was Sockets eigentlich sind btw. wie und wann man sie wofür benutzt. Smile Geöffnete Sockets (bzw. verbundene) sind bei einem Verbindungsabbruch nicht ungültig, sondern schlicht nicht verbunden. Deshalb müsst ihr auch dafür Sorge tragen (schade, dass Blitz das nicht macht), dass sie tatsächlich korrekt gelöscht werden (und nicht nur getrennt).
 

Mau

BeitragMi, Sep 21, 2005 17:21
Antworten mit Zitat
Benutzer-Profile anzeigen
Tja, wenn aber dann ein Client sein Fenster einfach schliesst, ist der Host gearscht -dann schmiert das Game beim Schliessen des Servers ab -einzige Lösung wäre hier vielleicht, regelmässig ein Ping anzufordern, ob der Client noch da ist. Kommt keins, wird der Stream geschlossen.

Noch eine Frage in Sachen IP: ich möchte in meinem Game dem Host gleich seine IP anzeigen, damit auch unbedarfte Spieler die gleich ihren Freunden mitteilen können. Ich ermittle die IP des Hosts momentan so:

Code: [AUSKLAPPEN]

CountHostIPs("")
HostIP      = HostIP(1)
HostIP_Str$ = DottedIP$( HostIP(1) )


Funktioniert auch -AAABER: woher weiss man denn, welches die lokale, und welched die Internet-IP ist?
Alles muss, nichts kann!
  • Zuletzt bearbeitet von Mau am Mi, Sep 21, 2005 17:44, insgesamt einmal bearbeitet
 

Nox

BeitragMi, Sep 21, 2005 17:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Mau hat Folgendes geschrieben:
Tja, wenn aber dann ein Client sein Fenster einfach schliesst, ist der Host gearscht -dann schmiert das Game beim Schliessen des Servers ab -einzige Lösung wäre hier vielleicht, regelmässig ein Ping anzufordern, ob der Client noch da ist. Kommt keins, wird der Stream geschlossen.


Auch wenn du beim Schließen der Server-Applikation alle Sockets der Clients schließt? Kann ich mir schon fast nicht mehr vorstellen.
Ansonsten: Ein Ping/Pong ist Pflicht! Ein Host würde z.B. nicht mitbekommen, wenn ein Client sein Netzwerkkabel zieht und sofort die Internetverbindung verliert. Da bleibt dem Client nämlich keine Möglichkeit mehr, dem Host mitzuteilen, dass er grad stirbt. Wink
Also immer schön pingen, und z.B. nach drei nicht mehr empfangenen Pongs Verbindung trennen und Socket schließen (vom Client, natürlich).

Mau hat Folgendes geschrieben:
Woher weiss man nun, was eine interne IP und was z.B. eine Internet-IP ist? Und wie viele IPs kann ein Host haben?


Das weiß man so leicht natürlich nicht. Smile Allerdings hätte ich da eine andere Möglichkeit - wenn auch aufwendiger:
Verbinde beispielweise nach "checkip.dyndns.org" per TCP auf Port 80 und sende ein einfaches HTTP-Request:
GET / HTTP/1.1
(zwei Leerzeilen)

Als Antwort wird der Host dir deine IP (mitsamt einigem Headerkram, den du getrost ignorieren kannst) mitteilen. Schaue dir die Antwort auf deinen Request einfach mal an. Daraus wirst du ableiten können, wie du die IP rausfiltern kannst.
 

Mau

BeitragMi, Sep 21, 2005 17:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Niaaa... ich linke ungern auf irgendwelche Seiten. Irgendwann gibt's die nicht mehr und es hagelt hässliche Fehlermeldungen oder es kommt nix. Lieber unabhängig bleiben Very Happy Ich habe es jetzt so gemacht, daß ich dem Host einfach seine IPs aufliste und ihm mitteile, er soll alle durchprobieren. Harr! Die meisten haben eh nur zwei, also wird das zu verkraften sein. Wink

EDIT: Also, diese "Einfrierer" machen mir wirklich sorgen -jedesmal, wenn z.B. der Host eine Nachricht (TCP) empfängt, stockt alles für ein, zwei Sekunden. Das ist ja furchtbar! Shocked
Alles muss, nichts kann!
 

Nox

BeitragMi, Sep 21, 2005 22:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Okay, die Seiten könnten natürlich irgendwann offline gehen (was beim Fall dyndns.org wahrscheinlich noch SEHR lange dauern könnte Wink ), ist ein Argument. Alle IPs zu listen und senden ist allerdings keine Lösung. Ich beispielweise sitzen hinter einem Hardware-Router und habe definitiv keine Internet-IP gelistet. Smile
Eine andere Möglichkeit wäre, den Benutzer das einfach selbst wählen lassen zu können. Vielleicht hat der Jemand eine dynamische Host, deren Name du zu einer IP auflösen kannst.
 

Mau

BeitragMi, Sep 21, 2005 23:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Ja klar, die IP kann der Host schon nach Belieben selbst eingeben -ich möchte damit nur ein wenig Hilfestellung geben, falls der User keinen Schimmer hat, was für eine IP er hat (oder damit er nicht extra nachsehen muss). Das passt schon.

Was mir im Moment aber Angst macht, ist dieses ständige "Einfrieren" des Hosts, wenn z.B. eine Client-Nachricht reinkommt (Nachrichteneingang wird gelesen mit ReadAvail() -und die Nachrichten sind wirklich nicht groß, ein ID-Byte und dann halt noch ein Chat-String). Aber jedesmal hält das komplette Programm für ein, zwei Sekunden an, wenn eine Nachricht reinkommt. Allerdings nur bei Verbindung über Internet. Über LAN ist das auch noch bemerkbar, aber nicht ganz so stark.

Dagegen gibt es wohl keine Lösung, oder?
Alles muss, nichts kann!
 

Nox

BeitragDo, Sep 22, 2005 20:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Also, ich habe es jetzt mal selbst ausprobiert. Konnte natürlich nur lokal testen, da aber ohne jegliche Geschwindigkeitseinbußen. Teste es doch einfach mal bei dir. Hier die beiden Quellcodes (einmal ein Serverprogramm und einmal ein Clientprogramm):

Server:
BlitzBasic: [AUSKLAPPEN]

; SERVER

Global client% = 0
Global socket% = 0
Global msg$ = \"\"
Const port% = 1234

; Initialization and startup ----------------------------------------------------------------

Graphics3D( 640, 480, 16, 2 )
AppTitle( \"Server\" )

Print( \"Starting...\" )
socket% = CreateTCPServer( port% ) ; Try to listen on a specific port.

If( Not socket% ) Then ; Test if listening was successful.
Print( \"Failed to listen on port \"+ port% + \"!\" )
WaitKey()
End()
EndIf

Print( \"Ready. Hit <ESC> to exit.\" )
Print( \"Hit <SPACE> to send a message to the client.\" )

; Mainloop ----------------------------------------------------------------------------------
Local accept%

While( Not KeyHit( 1 ) )

accept% = AcceptTCPStream( socket% ) ; Test if a connection is pending.
If( accept% ) Then

If( client% ) Then ; Is a client already connected?
CloseTCPStream( accept% ) ; Drop the connection.
Print( \"Dropped pending connection.\" )
Else
client% = accept% ; Attach socket identifier.
Print( \"Accepted pending connection.\" )
WriteString( client%, \"Hello there!\" ) ; Send something.
EndIf
EndIf

If( KeyDown( 57 ) And client% > 0 ) Then ; Does the user want to send a message?
msg$ = Input( \"Enter message to be sent:\" )
WriteString( client%, msg$ )
EndIf

Wend


; Shutdown process --------------------------------------------------------------------------

If( client% ) Then
CloseTCPStream( client% )
EndIf

If( socket% ) Then
CloseTCPServer( socket% )
EndIf


Client:
BlitzBasic: [AUSKLAPPEN]

; CLIENT
Global host$ = \"localhost\"
Global port% = 1234
Global socket% = 0
Global msglen% = 0
Global sread$ = \"\"

; Initialization and startup ----------------------------------------------------------------

Graphics3D( 640, 480, 16, 2 )
AppTitle( \"Client\" )

Print( \"Starting...\" )
socket% = OpenTCPStream( host$, port% ) ; Try to connect to a host.
TCPTimeouts( 1, 0 ) ; We want a \"state machine\". Wink

If( Not socket% ) Then ; Test if connection was successful.
Print( \"Failed to connect to \"+ host$ + \", port \"+ port% + \"!\" )
WaitKey()
End()
EndIf

Print( \"Connected. Hit <ESC> to exit.\" )

; Mainloop ----------------------------------------------------------------------------------
Local accept%

While( Not KeyHit( 1 ) )

msglen% = ReadAvail( socket% ) ; Get amount of data is pending.

If( msglen% > 0 ) ; If there's data pending, receive it.
sread$ = ReadString( socket% )
Print( \"Received data: \"+ sread$ ) ; Print the received data.
sread$ = \"\" ; Reset variable to clear the local buffer.
EndIf

Wend


; Shutdown process --------------------------------------------------------------------------

If( socket% ) Then
CloseTCPStream( socket% )
EndIf


Edit: Ich denke übrigens, dass du möglicherweise nicht beachtet hast, ein vernünftiges Timeout zu setzen. Probiert habe ich nicht, ob eine "0" auch zulässig ist. Bei ersten Tests funktionierte dies nicht.

Jolinah

BeitragDo, Sep 22, 2005 22:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Du kannst auch ohne Ping/Pong testen ob ein Socket nicht mehr verbunden ist mit Eof(). So erkennst du beim Client auch ob das Programm richtig beendet oder ob das Fenster geschlossen wurde.

Siehe Code oben Wink Vom Speed her lief es auch ohne Probleme bei mir.
 

Mau

BeitragFr, Sep 23, 2005 9:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Bei einem Text-Screen erkennt man Ruckler doch kaum Very Happy

In meinem Game ist ziemlich viel Bewegung auf dem Screen (animierte 3D-Figuren, Laufschrift, Kamera usw.). Sobald eine Nachricht reinkommt, fällt das da brutal auf. Das kann man mit einem Text-Programm ja kaum merken -der schreibt den Text ja erst, wenn er fertig ist. Es liegt auch nicht an einer Schleife, die ich zu lange durchlaufe. Die einzige Schleife, die ich in der Empfangs-Funktion habe, ist das While ReadAvail( ) ... Wend , es muß an den TCP-Funktionen liegen.

@Jolinah: Du meinst, mann könne statt einem Ping auch AcceptTCPStream( ) nutzen, um zu sehen, ob ein CLient noch da ist? Ich denke, daß liefert nur einen Wert, wenn gerade eine Nachricht vom Client eintrifft, also nicht ständig. Als Ping-Ersatz könnte man es ja nur nutzen, wenn es ständig einen Wert liefert, so lange eine Verbindung offen ist. Oder meintest du etwas anderes?
Alles muss, nichts kann!
 

Nox

BeitragSa, Sep 24, 2005 11:29
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn es bei der Textvariante nicht ruckelt, dann wird es höchstwahrscheinlich nicht an der Datenübertragung von TCP liegen. Mache doch erstmal Tests NUR mit TCP, wie mein Beispiel. Nur mit etwas mehr Belastung. Wenn die Tests ebenfalls alle funktionieren, weißt du vielleicht mehr.

Edit: Bist du sicher, dass du einen korrekten Timeout zu verwendest?
 

Mau

BeitragSa, Sep 24, 2005 13:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Das witzige ist ja, daß TCPTimeOuts überhaupt gar keinen Effekt zu haben scheint. Das bringt keinen Unterschied. Ich frage mich, ob das nicht einfach nur ein "Fake"-Befehl ist Very Happy
Alles muss, nichts kann!
 

Nox

BeitragSa, Sep 24, 2005 15:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Ist es mit Sicherheit nicht. Bei mir hat die Funktion Effekt gezeigt. Ohne den Befehl in meinem Code wurde jeweils 10 Sek. gewartet, ob Daten eintreffen.

Jolinah

BeitragSo, Sep 25, 2005 1:30
Antworten mit Zitat
Benutzer-Profile anzeigen
@Mau: Ich meinte die Eof()-Funktion nicht AcceptTCPStream().
Bei TCP wird ja ReadAvail() benutzt zum schauen wieviel Daten angekommen sind, mit Eof() kann man hingegen Testen ob das Socket noch verbunden ist. Wenn Eof() -1 zurück gibt dann war es eine unsaubere Trennung (Trennung ohne CloseTCPStream()), bei 1 war es eine saubere Trennung. Also kannst du bevor du was machst immer Abfragen ob das Socket noch verbunden ist.

PS: Kann mir irgendwie nicht vorstellen dass es an TCP liegt. Klar TCP ist langsamer als UDP, aber so extrem ist es nun auch wieder nicht, zumindest solange man nicht Byte für Byte sendet, weil da immer der ganze TCP Header mit verschickt wird. Wollte früher mal nen 3D-Chat machen wo sich die Chatter als 3D-Charakter bewegen können usw.. Hab da TCP und UDP kombiniert. Bewegungsupdates mit UDP und Chat etc. mit TCP. Ruckler seitens TCP hatte ich aber keine, höchstens von der Grafik her Wink

Das Verbinden kann manchmal ne Weile dauern, aber wenn man einmal verbunden ist treten zumindest bei mir keine solchen Wartezeiten mehr auf.
 

Mau

BeitragSo, Sep 25, 2005 10:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe jetzt mal das

While ReadAvail(Stream) > 0

gegen

While EOF(Stream) = 0

getauscht -was ja eigentlich funktionieren müsste, oder? Seltsamerweise hängt sich das Spiel dann in einer Endlosschleife auf, selbst wenn keine Daten reinkommen. Kann es sein, daß ein vorhandener Stream IMMER ein EOF 0 liefert, auch wenn keine Daten von ihm kommen?

Laut Manual müsste EOF doch nur 0 liefern, wenn Daten aus dem Strom zu lesen sind und 1, wenn er "leergelesen" ist, oder?
Alles muss, nichts kann!

Gehe zu Seite Zurück  1, 2, 3  Weiter

Neue Antwort erstellen


Übersicht BlitzBasic Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group