WebSocket Chatclient
-
- Beiträge: 659
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: WebSocket Chatclient
Leider hängt sich der Server immer wieder auf und reagiert nicht mehr, sobald mehrere Client dran sind.
Ich gehe mal stark davon aus, dass das an den Threads liegt.
Nun brauche ich keinen Server, der hunderte Clients managen kann und da ich das mit den Threads und dem Mutex im Leben nicht verstehe, würde ich mich sehr freuen, wenn mir jemand zeigen kann, wie ich den Servercode ohne Threads bekomme.
Ich gehe mal stark davon aus, dass das an den Threads liegt.
Nun brauche ich keinen Server, der hunderte Clients managen kann und da ich das mit den Threads und dem Mutex im Leben nicht verstehe, würde ich mich sehr freuen, wenn mir jemand zeigen kann, wie ich den Servercode ohne Threads bekomme.
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
Re: WebSocket Chatclient
Da fehlte glaub ich im Event Handling ein Mutex..
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
Re: WebSocket Chatclient
Ich hab den Server gerade mit über 100 Verbindungen getestet, das ging so weit ohne Probleme (Unter Windows). Aber ich glaube einen Fall gefunden zu haben, der unter Linux zu einem Deadlock führt.stevie1401 hat geschrieben:Leider hängt sich der Server immer wieder auf und reagiert nicht mehr, sobald mehrere Client dran sind.
Ich gehe mal stark davon aus, dass das an den Threads liegt.
Deswegen bin ich gerade dabei eine VM aufzusetzen, um das Ganze zu testen. Falls du also Linux/MacOS verwendest, dann stehen die Chancen gut, dass ich gleich eine neue Version mit dem Bugfix hochlade.
-
- Beiträge: 659
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: WebSocket Chatclient
@Dadido3, ich habe es mit Linux und Windows in allen Variationen getestet. Der Fehler war immer der gleiche.
Einige Erklärungen was ich gemachte habe im Code unter Case "Message"
Sollte es programmtechnisch FALSCH sein was ich gemacht habe, wäre über jede Hilfe Dankbar.
Noch einmal zur Erklärung.
Ich lasse über diesen Code einen Doppelkopf-Server laufen.
Alles was mit Doppelkopf zu tun hat, Spieler, Karten, alles, managet die Procedure Checknachricht.
Insofern ist es egal, ob dieser Server für einen Chat gemacht ist.
Relevant ist, dass Daten zuverlässeig vom Client zum Server und zurück kommen.
Ich kann manchmal mit 30 Spielern 1 Std spielen, aber die Regel ist, dass der Server nach wenigen Minuten einfach nicht mehr erreichbar ist und einfach nichts mehr macht.
In einer Leelaufschleife scheint er nicht zu sein, denn die CPU-Last ist bei rund 0-1 %
Edith:
Ich benutze testweise gerade diesen Code:
Ich habe diesen Code ohne Thread hinbekommen.
Funktioniert tadellos, zumindest ohne Abstürze oder Hänger - allerdings kommen nicht immer alle Nachrichten an.
Das war im ersten Code erheblich besser.
__________________________________________________
Quote-Tags>Code-Tags
28.02.2018
RSBasic
Einige Erklärungen was ich gemachte habe im Code unter Case "Message"
Sollte es programmtechnisch FALSCH sein was ich gemacht habe, wäre über jede Hilfe Dankbar.
Noch einmal zur Erklärung.
Ich lasse über diesen Code einen Doppelkopf-Server laufen.
Alles was mit Doppelkopf zu tun hat, Spieler, Karten, alles, managet die Procedure Checknachricht.
Insofern ist es egal, ob dieser Server für einen Chat gemacht ist.
Relevant ist, dass Daten zuverlässeig vom Client zum Server und zurück kommen.
Ich kann manchmal mit 30 Spielern 1 Std spielen, aber die Regel ist, dass der Server nach wenigen Minuten einfach nicht mehr erreichbar ist und einfach nichts mehr macht.
In einer Leelaufschleife scheint er nicht zu sein, denn die CPU-Last ist bei rund 0-1 %
Code: Alles auswählen
Procedure WebSocket_Event(*Server, *Client, Event, *Event_Frame.WebSocket_Server::Event_Frame)
Protected Chat_Message.Chat_Message
Protected Chat_Username_Change.Chat_Username_Change
Protected Chat_Userlist.Chat_Userlist
Protected JSON_ID.i
Protected JSON2_ID.i
LockMutex(Mutex)
Select Event
Case WebSocket_Server::#Event_Connect
PrintN(" #### Client connected: " + *Client)
AddElement(Client())
Client()\WebSocket_Client = *Client
JSON2_ID = CreateJSON(#PB_Any)
If JSON2_ID
Chat_Userlist\Type = "Userlist"
ForEach Client()
AddElement(Chat_Userlist\UserName())
Chat_Userlist\UserName() = Client()\Username
Next
InsertJSONStructure(JSONValue(JSON2_ID), Chat_Userlist, Chat_Userlist)
WebSocket_Server::Frame_Text_Send(*Server, *Client, ComposeJSON(JSON2_ID))
FreeJSON(JSON2_ID)
EndIf
Case WebSocket_Server::#Event_Disconnect
PrintN(" #### Client disconnected: " + *Client)
ForEach Client()
If Client()\WebSocket_Client = *Client
DeleteElement(Client())
Break
EndIf
Next
JSON2_ID = CreateJSON(#PB_Any)
If JSON2_ID
Chat_Userlist\Type = "Userlist"
ForEach Client()
AddElement(Chat_Userlist\UserName())
Chat_Userlist\UserName() = Client()\Username
Next
InsertJSONStructure(JSONValue(JSON2_ID), Chat_Userlist, Chat_Userlist)
ForEach Client()
WebSocket_Server::Frame_Text_Send(*Server, Client()\WebSocket_Client, ComposeJSON(JSON2_ID))
Next
FreeJSON(JSON2_ID)
EndIf
Case WebSocket_Server::#Event_Frame
Select *Event_Frame\Opcode
Case WebSocket_Server::#Opcode_Ping
PrintN(" #### Ping from *Client " + *Client)
Case WebSocket_Server::#Opcode_Text
JSON_ID = CatchJSON(#PB_Any, *Event_Frame\Payload, *Event_Frame\Payload_Size)
If JSON_ID
Select GetJSONString(GetJSONMember(JSONValue(JSON_ID), "Type"))
Case "Message"
ExtractJSONStructure(JSONValue(JSON_ID), Chat_Message, Chat_Message)
PrintN(Chat_Message\Author + ": " + Chat_Message\Message)
Debug PeekS(*Event_Frame\Payload, *Event_Frame\Payload_Size, #PB_UTF8)
;Ich kann verste das mit dem JSON nicht, deshalb nenutze ich es nicht
;Relevant ist mich mich "Chat_Message"
;Hier baue ich meine eigene Procedure ein:
;In dieser Procedure wird alles geregelt, was ich benötige
CheckNachricht(*client,Chat_Message)
;Aus "Checknachricht() heraus sende ich auch an die einzelnen Clients.
;Da die Procedure WebSocket_Event() am anfang ein LockMutex(Mutex) benutzt,
;gehe ich davon aus, dass alles was ich innerhalb Checknachricht mache auch geschützt ist.
;Ich benutze innerhalb Checknachricht() KEIN LockMutex(Mutex) - UnLockMutex(Mutex)
Case "Username_Change"
ExtractJSONStructure(JSONValue(JSON_ID), Chat_Username_Change, Chat_Username_Change)
ForEach Client()
If Client()\WebSocket_Client = *Client
Client()\Username = Chat_Username_Change\Username
Break
EndIf
Next
JSON2_ID = CreateJSON(#PB_Any)
If JSON2_ID
Chat_Userlist\Type = "Userlist"
ForEach Client()
AddElement(Chat_Userlist\UserName())
Chat_Userlist\UserName() = Client()\Username
Next
InsertJSONStructure(JSONValue(JSON2_ID), Chat_Userlist, Chat_Userlist)
ForEach Client()
WebSocket_Server::Frame_Text_Send(*Server, Client()\WebSocket_Client, ComposeJSON(JSON2_ID))
Next
FreeJSON(JSON2_ID)
EndIf
EndSelect
FreeJSON(JSON_ID)
EndIf
EndSelect
EndSelect
UnlockMutex(Mutex)
EndProcedure
Edith:
Ich benutze testweise gerade diesen Code:
Code: Alles auswählen
Procedure WebSocket_Server(*Server.ServerStructure)
Protected.i SEvent, ClientID, handshake, i, ClientIP, GadgetItem, ReceivedBytes
Protected *Buffer
Protected Header$, Key$, Accept$, Body$
Protected Byte.a
Protected.i Fin, Opcode, Masked, Payload, Ptr, n, Pos1, Pos2
Protected Dim MaskKey.a(3)
Protected j,k
Protected.s s
Debug "Server"
*Buffer = AllocateMemory(10240)
If *Buffer
Repeat
SEvent = NetworkServerEvent()
If SEvent
ClientID = EventClient()
ClientIP = GetClientIP(ClientID)
Select SEvent
Case #PB_NetworkEvent_Connect
AddMapElement(*Server\Client(), Str(ClientID))
;StatusBarText(0, 1, "Clients: " + Str(MapSize(*Server\Client())), #PB_StatusBar_Center)
;DisableGadget(#TransmitButton, #False)
;DisableGadget(#DisconnectButton, #False)
;AddGadgetItem(#TransmitCombo, -1, Str(ClientID))
;AddGadgetItem(#TransmitCombo, -1, IPString(ClientIP) + " - " + Str(ClientID))
;GadgetItem = CountGadgetItems(#TransmitCombo) - 1
;SetGadgetItemData(#TransmitCombo, GadgetItem, ClientID)
;SetGadgetState(#TransmitCombo, GadgetItem)
Debug "A new client has connected !"
Case #PB_NetworkEvent_Data
FillMemory(*Buffer, 10000)
ReceivedBytes = ReceiveNetworkData(ClientID, *Buffer, 1000)
Debug "Recv: " + Str(ReceivedBytes)
If Not *Server\Client(Str(ClientID))\Handshake
Header$ = PeekS(*Buffer, ReceivedBytes, #PB_UTF8)
Pos1 = FindString(Header$, "Sec-WebSocket-Key: ")
If Pos1
Pos1 + 19
Pos2 = FindString(Header$, #CRLF$, Pos1)
If Pos2
Key$ = Trim(Mid(Header$, Pos1, Pos2 - Pos1))
Accept$ = SecWebsocketAccept(Key$)
EndIf
EndIf
Header$ = "HTTP/1.1 101 Switching Protocols" + #CRLF$
Header$ + "Upgrade: WebSocket"+ #CRLF$
Header$ + "Connection: Upgrade"+ #CRLF$
Header$ + "Sec-WebSocket-Accept: " + Accept$ + #CRLF$
Header$ + #CRLF$
SendNetworkString(ClientID, Header$)
*Server\Client(Str(ClientID))\Handshake = #True
Else
Ptr = 0
Byte = PeekA(*Buffer + Ptr)
If Byte & %10000000
Fin = #True
Else
Fin = #False
EndIf
Opcode = Byte & %00001111
Ptr = 1
Debug "Fin:" + Str(Fin)
Debug "Opcode: " + Str(Opcode)
Byte = PeekA(*Buffer + Ptr)
Masked = Byte >> 7
Payload = Byte & $7F
Ptr + 1
If Payload = 126
Payload = PeekA(*Buffer + Ptr) << 8
Ptr + 1
Payload | PeekA(*Buffer + Ptr)
Ptr + 1
ElseIf Payload = 127
Payload = 0
n = 7
For i = Ptr To Ptr + 7
Payload | PeekA(*Buffer + i) << (8 * n)
n - 1
Next i
Ptr + 8
EndIf
Debug "Masked: " + Str(Masked)
Debug "Payload: " + Str(Payload)
If Masked
n = 0
For i = Ptr To Ptr + 3
MaskKey(n) = PeekA(*Buffer + i)
Debug "MaskKey " + Str(n + 1) + ": " + RSet(Hex(MaskKey(n)), 2, "0")
n + 1
Next i
Ptr + 4
EndIf
Select Opcode
Case #TextFrame
If Masked
n = 0
For i = Ptr To Ptr + Payload - 1
PokeA(*Buffer + i, PeekA(*Buffer + i) ! MaskKey(n % 4))
n + 1
Next i
EndIf
Body$ = PeekS(*Buffer + Ptr, Payload, #PB_UTF8)
;AddGadgetItem(#ReceiveEdit, -1, IPString(ClientIP) + "-" + Str(ClientID) + ": " + Body$)
Debug "Text vom client: "+body$
checknachricht(ClientID,Body$)
Case #ConnectionCloseFrame
WebSocket_ClientDisconnect(ClientID, *Server)
Case #PingFrame
Byte = PeekA(*Buffer) & %11110000
PokeA(*Buffer, Byte | #PongFrame)
SendNetworkData(ClientID, *Buffer, ReceivedBytes)
Default
Debug "Opcode not implemented yet!"
EndSelect
EndIf
Case #PB_NetworkEvent_Disconnect
WebSocket_ClientDisconnect(ClientID, *Server)
EndSelect
Else
Delay(10)
EndIf
ForEver
FreeMemory(*Buffer)
EndIf
EndProcedure
Funktioniert tadellos, zumindest ohne Abstürze oder Hänger - allerdings kommen nicht immer alle Nachrichten an.
Das war im ersten Code erheblich besser.
__________________________________________________
Quote-Tags>Code-Tags
28.02.2018
RSBasic
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
Re: WebSocket Chatclient
Ich habe es nun unter Ubuntu getestet, aber ich konnte den Fehler auch dort nicht reproduzieren. Nichtsdestotrotz hab ich einen möglichen Deadlock behoben, und in der README beschrieben wie man die Events über polling abfragen kann. (Intern werden immernoch threads verwendet, aber das hat einen dann nicht mehr zu interessieren)
Am Besten ist es du benutzt die Polling-Methode, so wird alles nacheinander abgearbeitet.
Desweiteren wäre es wahrscheinlich besser über Probleme mit dem Include und dessen Benutzung im jeweiligen Thread zu schreiben.
Der Mutex ist auch innerhalb deiner Funktion aktiv, er hat aber keine Bedeutung, wenn du in deinem Hauptprogramm, ohne diesen Mutex zu benutzen, auf irgendwelche Ressourcen/Listen/Speicher zugreifst, welche du auch in Checknachricht() benutzt.stevie1401 hat geschrieben:Code: Alles auswählen
;Da die Procedure WebSocket_Event() am anfang ein LockMutex(Mutex) benutzt, ;gehe ich davon aus, dass alles was ich innerhalb Checknachricht mache auch geschützt ist. ;Ich benutze innerhalb Checknachricht() KEIN LockMutex(Mutex) - UnLockMutex(Mutex)
Am Besten ist es du benutzt die Polling-Methode, so wird alles nacheinander abgearbeitet.
Desweiteren wäre es wahrscheinlich besser über Probleme mit dem Include und dessen Benutzung im jeweiligen Thread zu schreiben.
-
- Beiträge: 659
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: WebSocket Chatclient
Erst einmal vielen Dank für deine fleissige Arbeit.
In deinem Example Chat Server Polling kann ich leider nicht erkennen was Polling ist, ob das ein Befehl, eine Procedure oder was auch immer ist. Das Wort Polling kommt in dem Beispiel nicht vor.
Die README ist leider nicht lesbar.
Vielleicht ginbt es ja eine einfache Antwort.
Wie kann ich die Procedure "checknachricht" schützen, dass ich in dieser auf globale Variablen zugreifen kann ohne dass es zu Abstürzen kommt?
In deinem Example Chat Server Polling kann ich leider nicht erkennen was Polling ist, ob das ein Befehl, eine Procedure oder was auch immer ist. Das Wort Polling kommt in dem Beispiel nicht vor.
Die README ist leider nicht lesbar.
Vielleicht ginbt es ja eine einfache Antwort.
Wie kann ich die Procedure "checknachricht" schützen, dass ich in dieser auf globale Variablen zugreifen kann ohne dass es zu Abstürzen kommt?
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
Re: WebSocket Chatclient
Du benutzt das Polling indem du
zu
abänderst.
Und dann in deine Hauptschleifeeinfügst.
Dadurch wird dein Event-Handler nicht mehr von einem Thread, sondern von deinem Hauptprogramm aufgerufen. Du brauchst dann auch nichts weiter zu schützen.
Code: Alles auswählen
*Server = WebSocket_Server::Create(8090, @WebSocket_Event())
Code: Alles auswählen
*Server = WebSocket_Server::Create(8090)
Und dann in deine Hauptschleife
Code: Alles auswählen
While WebSocket_Server::Event_Callback(*Server, @WebSocket_Event())
Wend
Dadurch wird dein Event-Handler nicht mehr von einem Thread, sondern von deinem Hauptprogramm aufgerufen. Du brauchst dann auch nichts weiter zu schützen.
-
- Beiträge: 659
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: WebSocket Chatclient
Vielen herzlichen Dank!
habs auch gleich getestet.
Die 20 Dummyspieler konnten problemlos spielen.
Wenn allerdings 2 oder 3 Spieler fast gleichzeitig den Server disconnecten, kommt folgende Meldung:
[22:53:20] Executable-Typ: Windows - x64 (64bit, Unicode, Thread)
[22:53:20] Executable gestartet.
[23:17:31] [ERROR] WebSocket_Server.pbi (Zeile: 814)
[23:17:31] [ERROR] Das angegebene 'ConnectionID' ist null.
habs auch gleich getestet.
Die 20 Dummyspieler konnten problemlos spielen.
Wenn allerdings 2 oder 3 Spieler fast gleichzeitig den Server disconnecten, kommt folgende Meldung:
[22:53:20] Executable-Typ: Windows - x64 (64bit, Unicode, Thread)
[22:53:20] Executable gestartet.
[23:17:31] [ERROR] WebSocket_Server.pbi (Zeile: 814)
[23:17:31] [ERROR] Das angegebene 'ConnectionID' ist null.
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
-
- Beiträge: 659
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: WebSocket Chatclient
Läuft jetzt per-fekt!
Du bist TOP!
Vielen, vielen Dank für deine Hilfe
Du bist TOP!
Vielen, vielen Dank für deine Hilfe
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x