Ja, ein HTLM bzw. Javascript Code, der mir nichts nützt, da ich einen Purebasic-Code brauche.War bei dem Projekt nicht auch auch Beispiel Client dabei?
Websocket Client
-
- Beiträge: 700
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: Websocket Client
Das ist sehr nett von dir, aber das ist der SERVER, ich brauche einen Client-Code
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
Re: Websocket Client
Vielleicht hilft dir das weiter: https://gist.github.com/Netzvamp/8623def14501de15c9e4
"Papa, ich laufe schneller - dann ist es nicht so weit."
-
- Beiträge: 700
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: Websocket Client
Auch das kenne ich schon. Das funktioniert nicht.
Der Client stürzt ständig mit einem Speicherfehler ab.
Der Client stürzt ständig mit einem Speicherfehler ab.
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
-
- Beiträge: 700
- Registriert: 19.10.2014 15:51
- Kontaktdaten:
Re: Websocket Client
Neuer Versuch, ich habe mich zwar im englischen Forum an ein Thema rangeklemmt, aber dort antwortet nniemand.
Ich habe einen testwebsocketserver aufgesetzt.
Accessible at ws://doko-cafe.de:8090
Ich benutze den Code von Dadio3:
Dann testete ich diesen Clientcode:
Bei Linux (PB6.21 beta 5) stürzt der Client ab mit folgender Nachrcht:
munmap_chunk(): invalid pointer
und unterbricht bei Zeile:
ProcedureReturn frame_typ
Bei Windows fubnktioniert es mit folgender Rückmeldung:
Unter "Userlist"} kommt ein weisses Fragezeichen auf schwarzem Hintergrund, welches ich hier nicht abbilden kann.
Was ist an dem Clientcode falsch? Und gibt es noch einen Linux-Bug?
Ich habe einen testwebsocketserver aufgesetzt.
Accessible at ws://doko-cafe.de:8090
Ich benutze den Code von Dadio3:
Code: Alles auswählen
Structure Chat_Message
Type.s
Author.s
Message.s
Timestamp.q
EndStructure
Structure Chat_Username_Change
Type.s
Username.s
EndStructure
Structure Chat_Userlist
Type.s
List Username.s()
EndStructure
Structure Client
*WebSocket_Client
Username.s
EndStructure
Global NewList Client.Client()
XIncludeFile "Includes/WebSocket_Server.pbi"
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
Protected JSON_String.s
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)
JSON_String = ComposeJSON(JSON2_ID)
ForEach Client()
WebSocket_Server::Frame_Text_Send(*Server, Client()\WebSocket_Client, JSON_String)
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 = ParseJSON(#PB_Any, PeekS(*Event_Frame\Payload, *Event_Frame\Payload_Size, #PB_UTF8|#PB_ByteLength))
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|#PB_ByteLength)
JSON2_ID = CreateJSON(#PB_Any)
If JSON2_ID
ForEach Client()
If Client()\WebSocket_Client = *Client
Chat_Message\Author = Client()\Username
;Chat_Message\Timestamp = Date()
Break
EndIf
Next
InsertJSONStructure(JSONValue(JSON2_ID), Chat_Message, Chat_Message)
JSON_String = ComposeJSON(JSON2_ID)
;Debug JSON_String
ForEach Client()
WebSocket_Server::Frame_Text_Send(*Server, Client()\WebSocket_Client, JSON_String)
Next
FreeJSON(JSON2_ID)
EndIf
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)
JSON_String = ComposeJSON(JSON2_ID)
ForEach Client()
WebSocket_Server::Frame_Text_Send(*Server, Client()\WebSocket_Client, JSON_String)
Next
FreeJSON(JSON2_ID)
EndIf
EndSelect
FreeJSON(JSON_ID)
EndIf
EndSelect
EndSelect
EndProcedure
OpenConsole()
*Server = WebSocket_Server::Create(8090)
Repeat
While WebSocket_Server::Event_Callback(*Server, @WebSocket_Event())
Wend
Delay(10)
ForEver
; IDE Options = PureBasic 6.21 Beta 5 (Linux - x64)
; ExecutableFormat = Console
; CursorPosition = 160
; EnableThread
; EnableXP
; Executable = testserver_polling
; CompileSourceDirectory
Dann testete ich diesen Clientcode:
Code: Alles auswählen
; Websocketclient by Netzvamp
; Version: 2016/01/08
PurifierGranularity(1, 1, 1, 1)
DeclareModule WebsocketClient
Declare OpenWebsocketConnection(URL.s)
Declare SendTextFrame(connection, message.s)
Declare ReceiveFrame(connection, *MsgBuffer)
Declare SetSSLProxy(ProxyServer.s = "", ProxyPort.l = 8182)
Enumeration
#frame_text
#frame_binary
#frame_closing
#frame_ping
#frame_unknown
EndEnumeration
EndDeclareModule
Module WebsocketClient
;TODO: Add function to send binary frame
;TODO: We don't support fragmetation right now
;TODO: We should send an closing frame, but server will also just close
;TODO: Support to send receive bigger frames
Declare Handshake(Connection, Servername.s, Path.s)
Declare ApplyMasking(Array Mask.a(1), *Buffer)
Global Proxy_Server.s, Proxy_Port.l
Macro dbg(txt)
CompilerIf #PB_Compiler_Debugger
Debug "WebsocketClient: " + FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss",Date()) + " > " + txt
CompilerEndIf
EndMacro
Procedure SetSSLProxy(ProxyServer.s = "", ProxyPort.l = 8182)
Proxy_Server.s = ProxyServer.s
Proxy_Port.l = ProxyPort.l
EndProcedure
Procedure OpenWebsocketConnection(URL.s)
Protokol.s = GetURLPart(URL.s, #PB_URL_Protocol)
Servername.s = GetURLPart(URL.s, #PB_URL_Site)
Port.l = Val(GetURLPart(URL.s, #PB_URL_Port))
If Port.l = 0 : Port.l = 80 : EndIf
Path.s = GetURLPart(URL.s, #PB_URL_Path)
If Path.s = "" : Path.s = "/" : EndIf
If Protokol.s = "wss" ; If we connect with encryption (https)
If Proxy_Port
Connection = OpenNetworkConnection(Proxy_Server.s, Proxy_Port.l, #PB_Network_TCP, 1000)
Else
dbg("We need an SSL-Proxy like stunnel for encryption. Configure the proxy with SetSSLProxy().")
EndIf
ElseIf Protokol.s = "ws"
Connection = OpenNetworkConnection(Servername.s, Port.l, #PB_Network_TCP, 1000)
EndIf
If Connection
If Handshake(Connection, Servername.s, Path.s)
dbg("Connection and Handshake ok")
ProcedureReturn Connection
Else
dbg("Handshake-Error")
ProcedureReturn #False
EndIf
Else
dbg("Couldn't connect")
ProcedureReturn #False
EndIf
EndProcedure
Procedure Handshake(Connection, Servername.s, Path.s)
Request.s = "GET /" + Path.s + " HTTP/1.1"+ #CRLF$ +
"Host: " + Servername.s + #CRLF$ +
"Upgrade: websocket" + #CRLF$ +
"Connection: Upgrade" + #CRLF$ +
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" + #CRLF$ +
"Sec-WebSocket-Version: 13" + #CRLF$ +
"User-Agent: CustomWebsocketClient"+ #CRLF$ + #CRLF$
SendNetworkString(Connection, Request.s, #PB_UTF8)
*Buffer = AllocateMemory(65536)
; We wait for answer
Repeat
Size = ReceiveNetworkData(connection, *Buffer, 65536)
Answer.s = Answer.s + PeekS(*Buffer, Size, #PB_UTF8)
If FindString(Answer, #CRLF$ + #CRLF$)
Break
EndIf
Until Size <> 65536
Answer.s = UCase(Answer.s)
; Check answer
If FindString(Answer.s, "HTTP/1.1 101") And FindString(Answer.s, "CONNECTION: UPGRADE") And FindString(Answer.s, "UPGRADE: WEBSOCKET")
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure ApplyMasking(Array Mask.a(1), *Buffer)
For i = 0 To MemorySize(*Buffer) - 1
PokeA(*Buffer + i, PeekA(*Buffer + i) ! Mask(i % 4))
Next
EndProcedure
Procedure SendTextFrame(connection, message.s)
; Put String in Buffer
MsgLength.l = StringByteLength(message.s, #PB_UTF8)
*MsgBuffer = AllocateMemory(MsgLength)
PokeS(*MsgBuffer, message.s, MsgLength, #PB_UTF8|#PB_String_NoZero)
dbg("Messagelength to send: " + Str(MsgLength))
; The Framebuffer, we fill with senddata
If MsgLength <= 125
Fieldlength = 6
ElseIf MsgLength >= 126 And MsgLength <= 65535
Fieldlength = 8
Else
Fieldlength = 14
EndIf
dbg("Fieldlength to send: " + Str(Fieldlength))
*FrameBuffer = AllocateMemory(Fieldlength + MsgLength)
; We generate 4 random masking bytes
Dim Mask.a(3)
Mask(0) = Random(255,0)
Mask(1) = Random(255,0)
Mask(2) = Random(255,0)
Mask(3) = Random(255,0)
pos = 0 ; The byteposotion in the framebuffer
; First Byte: FIN(1=finished with this Frame),RSV(0),RSV(0),RSV(0),OPCODE(4 byte)=0001(text)
PokeB(*FrameBuffer, %10000001) : pos + 1 ; = 129
; Second Byte: Masking(1),length(to 125bytes, else we have to extend)
If MsgLength <= 125 ; Length fits in first byte
PokeA(*Framebuffer + pos, MsgLength + 128) : pos + 1 ; + 128 for Masking
ElseIf MsgLength >= 126 And MsgLength <= 65535 ; We have to extend length to third byte
PokeA(*Framebuffer + pos, 126 + 128) : pos + 1 ; 126 for 2 extra length bytes and + 128 for Masking
PokeA(*FrameBuffer + pos, (MsgLength >> 8)) : pos + 1 ; First Byte
PokeA(*FrameBuffer + pos, MsgLength) : pos + 1 ; Second Byte
Else ; It's bigger than 65535, we also use 8 extra bytes
PokeA(*Framebuffer + pos, 127 + 128) : pos + 1 ; 127 for 8 extra length bytes and + 128 for Masking
PokeA(*Framebuffer + pos, 0) : pos + 1 ; 8 Bytes for payload lenght. We don't support giant packages for now, so first bytes are zero :P
PokeA(*Framebuffer + pos, 0) : pos + 1
PokeA(*Framebuffer + pos, 0) : pos + 1
PokeA(*Framebuffer + pos, 0) : pos + 1
PokeA(*Framebuffer + pos, MsgLength >> 24) : pos + 1
PokeA(*Framebuffer + pos, MsgLength >> 16) : pos + 1
PokeA(*Framebuffer + pos, MsgLength >> 8) : pos + 1
PokeA(*Framebuffer + pos, MsgLength) : pos + 1 ; = 10 Byte
EndIf
; Write Masking Bytes
PokeA(*FrameBuffer + pos, Mask(0)) : pos + 1
PokeA(*FrameBuffer + pos, Mask(1)) : pos + 1
PokeA(*FrameBuffer + pos, Mask(2)) : pos + 1
PokeA(*FrameBuffer + pos, Mask(3)) : pos + 1
ApplyMasking(Mask(), *MsgBuffer)
CopyMemory(*MsgBuffer, *FrameBuffer + pos, MsgLength)
;For x = 0 To 100 Step 5
;Debug Str(PeekA(*FrameBuffer + x)) + " | " + Str(PeekA(*FrameBuffer + x + 1)) + " | " + Str(PeekA(*FrameBuffer + x + 2)) + " | " + Str(PeekA(*FrameBuffer + x + 3)) + " | " + Str(PeekA(*FrameBuffer + x + 4))
;Next
If SendNetworkData(connection, *FrameBuffer, Fieldlength + MsgLength) = Fieldlength + MsgLength
dbg("Textframe send, Bytes: " + Str(Fieldlength + MsgLength))
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure ReceiveFrame(connection, *MsgBuffer)
*FrameBuffer = AllocateMemory(65536)
Size = ReceiveNetworkData(connection, *FrameBuffer, 65536)
; Repeat
; *FrameBuffer = ReAllocateMemory(*FrameBuffer, 65536)
; Size = ReceiveNetworkData(connection, *FrameBuffer, 65536)
; ;Answer.s = Answer.s + PeekS(*FrameBuffer, Size, #PB_UTF8)
; Until Size <> 65536
;
dbg("Received Frame, Bytes: " + Str(Size))
*FrameBuffer = ReAllocateMemory(*FrameBuffer, Size)
; debug: output any single byte
If #PB_Compiler_Debugger
For x = 0 To Size - 1 Step 1
dbg_bytes.s + Str(PeekA(*FrameBuffer + x)) + " | "
Next
dbg(dbg_bytes)
EndIf
; Getting informations about package
If PeekA(*FrameBuffer) & %10000000 > #False
;dbg("Frame not fragmented")
fragmentation.b = #False
Else
dbg("Frame fragmented! This not supported for now!")
fragmentation.b = #True
EndIf
; Check for Opcodes
If PeekA(*FrameBuffer) = %10000001 ; Textframe
dbg("Text frame")
frame_typ.w = #frame_text
ElseIf PeekA(*FrameBuffer) = %10000010 ; Binary Frame
dbg("Binary frame")
frame_typ.w = #frame_binary
ElseIf PeekA(*FrameBuffer) = %10001000 ; Closing Frame
dbg("Closing frame")
frame_typ.w = #frame_closing
ElseIf PeekA(*FrameBuffer) = %10001001 ; Ping
; We just answer pings
*pongbuffer = AllocateMemory(2)
PokeA(*pongbuffer, 138)
PokeA(*pongbuffer+1, 0)
SendNetworkData(connection, *pongbuffer, 2)
dbg("Received Ping, answered with Pong")
frame_typ.w = #frame_ping
ProcedureReturn
Else
dbg("Opcode unknown")
frame_typ.w = #frame_unknown
ProcedureReturn #False
EndIf
; Check masking
If PeekA(*FrameBuffer + 1) & %10000000 = 128 : masking.b = #True : Else : masking.b = #False : EndIf
dbg("Masking: " + Str(masking))
pos.l = 1
; check size
If PeekA(*FrameBuffer + 1) & %01111111 <= 125 ; size is in this byte
frame_size.l = PeekA(*FrameBuffer + pos) & %01111111 : pos + 1
ElseIf PeekA(*FrameBuffer + 1) & %01111111 >= 126 ; Size is in 2 extra bytes
frame_size.l = PeekA(*FrameBuffer + 2) << 8 + PeekA(*FrameBuffer + 3) : pos + 2
EndIf
dbg("FrameSize: " + Str(frame_size.l))
If masking = #True
Dim Mask.a(3)
Mask(0) = PeekA(*FrameBuffer + pos) : pos + 1
Mask(1) = PeekA(*FrameBuffer + pos) : pos + 1
Mask(2) = PeekA(*FrameBuffer + pos) : pos + 1
Mask(3) = PeekA(*FrameBuffer + pos) : pos + 1
ReAllocateMemory(*MsgBuffer,frame_size)
CopyMemory(*FrameBuffer + pos, *MsgBuffer, frame_size)
ApplyMasking(Mask(), *MsgBuffer)
Else
ReAllocateMemory(*MsgBuffer,frame_size)
CopyMemory(*FrameBuffer + pos, *MsgBuffer, frame_size)
EndIf
ProcedureReturn frame_typ
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
; Minimal example to send and receive textmessages
; The preconfigured testserver "echo.websocket.org" will just echo back everything you've send.
;WebsocketClient::SetSSLProxy("https://doko-cafe.de",443)
Global connection
connection = WebsocketClient::OpenWebsocketConnection("ws://doko-cafe.de:8090")
; Proxy Setting:
; If you need an encyrpted connection (https/wss), you currently have to use an
; proxy software like stunnel (https://www.stunnel.org) to redirect unencrypted data into an encrypted connection
; Example stunnel.conf section:
; [websocket]
; client = yes
; accept = 127.0.0.1:8182
; connect = echo.websocket.org:443
;WebsocketClient::SetSSLProxy("127.0.0.1",1302)
;connect = "127.0.0.1:1302"
Repeat
If connection
NetworkEvent = NetworkClientEvent(connection)
Select NetworkEvent
Case #PB_NetworkEvent_Data
Debug "We've got Data"
*FrameBuffer = AllocateMemory(1)
Frametyp = WebsocketClient::ReceiveFrame(connection,*FrameBuffer)
If Frametyp = WebsocketClient::#frame_text
Debug "< " + PeekS(*FrameBuffer,MemoryStringLength(*FrameBuffer,#PB_UTF8|#PB_ByteLength),#PB_UTF8|#PB_ByteLength)
If WebsocketClient::SendTextFrame(connection, "HelloWorldServer.") = #False
Debug "Couldn't send. Are we disconnected?"
EndIf
ElseIf Frametyp = WebsocketClient::#frame_binary
Debug "< Received Binaryframe"
EndIf
Case #PB_NetworkEvent_Disconnect
If disconnected = #False
Debug "Disconnected"
EndIf
disconnected = #True
NetworkEvent = #PB_NetworkEvent_None
Case #PB_NetworkEvent_None
EndSelect
EndIf
Delay(1)
ForEver
CompilerEndIf
Bei Linux (PB6.21 beta 5) stürzt der Client ab mit folgender Nachrcht:
munmap_chunk(): invalid pointer
und unterbricht bei Zeile:
ProcedureReturn frame_typ
Bei Windows fubnktioniert es mit folgender Rückmeldung:
Code: Alles auswählen
WebsocketClient: 2025-04-15 08:20:12 > Connection and Handshake ok
We've got Data
WebsocketClient: 2025-04-15 08:20:12 > Received Frame, Bytes: 37
WebsocketClient: 2025-04-15 08:20:12 > 129 | 35 | 123 | 34 | 85 | 115 | 101 | 114 | 110 | 97 | 109 | 101 | 34 | 58 | 91 | 34 | 34 | 93 | 44 | 34 | 84 | 121 | 112 | 101 | 34 | 58 | 34 | 85 | 115 | 101 | 114 | 108 | 105 | 115 | 116 | 34 | 125 |
WebsocketClient: 2025-04-15 08:20:12 > Text frame
WebsocketClient: 2025-04-15 08:20:12 > Masking: 0
WebsocketClient: 2025-04-15 08:20:12 > FrameSize: 35
< {"Username":[""],"Type":"Userlist"}
Was ist an dem Clientcode falsch? Und gibt es noch einen Linux-Bug?
Ich programmiere nur noch mit Linux.
Linux Mint 21.x
Linux Mint 21.x
Re: Websocket Client
Ich habe den Client-Code nur mal kurz überflogen:
https://stackoverflow.com/questions/321 ... 4_32118638
- Es fehlen überall FreeMemory() aufrufe, das führt zu Speicherlecks.
- Bei ReAllocateMemory() wird blind die alte Speicheradresse weiterverwendet ohne den Rückgabewert zu beachten.
- Die vom Server empfangene frame_size wird ohne Überprüfung verwendet um aus einem Speicherbereich zu lesen.
- Die Rückgabewerte von SendNetworkData und ReceiveNetworkData werden ignoriert.
- Bei leerem Path wird anscheinend ein doppeltes / im Handshake gesendet.
- ...
https://stackoverflow.com/questions/321 ... 4_32118638