In this client module are a lot of bugs and memory leaks.
Bugfixes with integrated wss, added CloseConnection(), added SindBinaryFrame(), added Status at CloseConnection, allow unmasked transfers,
Code: Select all
; Websocketclient
;
; https://datatracker.ietf.org/doc/html/rfc6455
;
; by Netzvamp
; Version: 2016/01/08
;
; modified by infratec 2025/04/20
DeclareModule WebsocketClient
  
  #Opcode_Continue = $00
  #Opcode_Text = $01
  #Opcode_Binary = $02
  #Opcode_Closing = $08
  #Opcode_Ping = $09
  #Opcode_Pong = $0A
  #Opcode_Unknown = $FF
  
  
  Enumeration StatusCodes
    #StatusCode_NormalClosure = 1000
    #StatusCode_GoingAway
    #StatusCode_ProtocolError
    #StatusCode_NotAcceptedType
    #StatusCode_Reserved_1
    #StatusCode_Reserved_2
    #StatusCode_DontUse
    #StatusCode_MessageDoesNotFitType
    #StatusCode_PolicyViolation
    #StatusCode_MessageToBig
    #StatusCode_ServerDoesNotHandleTheNeededExtension
    #StatusCode_UnexpectedCondition
    
    #StatusCode_Reserved_3 = 1015
  EndEnumeration
  
  
  Structure WebsocketClient_Structure
    Connection.i
    Protokol$
    Servername$
    Path$
    Parameters$
    Port.i
    *ReceiveBuffer
    frame_fragmentation.i
    frame_masking.i
    frame_type.i
    frame_size.i
    Status.i
    Status$
  EndStructure
  
  
  Declare.i OpenConnection(*WebsocketClient.WebsocketClient_Structure, URL$)
  Declare.i SendPingFrame(*WebsocketClient.WebsocketClient_Structure)
  Declare.i SendTextFrame(*WebsocketClient.WebsocketClient_Structure, Text$, Masked.i=#True)
  Declare.i SendBinaryFrame(*WebsocketClient.WebsocketClient_Structure, *Payload, Masked.i=#True)
  Declare.i ReceiveFrame(*WebsocketClient.WebsocketClient_Structure)
  Declare CloseConnection(*WebsocketClient.WebsocketClient_Structure, Status.i=#StatusCode_NormalClosure)
  
EndDeclareModule
Module WebsocketClient
  
  EnableExplicit
  
  ;TODO: We don't support fragmetation right now
  ;TODO: Support to send/receive bigger frames
  
  Macro dbg(txt)
    CompilerIf #PB_Compiler_Debugger
      Debug "WebsocketClient: " + FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss",Date()) + " > " + txt
    CompilerEndIf
  EndMacro
  
  
  Declare.i SendCloseFrame(*WebsocketClient.WebsocketClient_Structure, Status.i=#StatusCode_NormalClosure)
  
  
  Procedure.i Handshake(*WebsocketClient.WebsocketClient_Structure)
    
    Protected Request$, *Buffer, Size.i, Answer$, TimeoutCounter.i, Ok.i, Result.i
    
    
    Request$ = "GET " + *WebsocketClient\Path$ + " HTTP/1.1"+ #CRLF$ +
               "Host: " + *WebsocketClient\Servername$ + #CRLF$ +
               "Upgrade: websocket" + #CRLF$ +
               "Connection: Upgrade" + #CRLF$ +
               "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" + #CRLF$ +
               "Sec-WebSocket-Version: 13" + #CRLF$ + #CRLF$
    
    Debug Request$
    
    SendNetworkString(*WebsocketClient\Connection, Request$)
    
    *Buffer = AllocateMemory(65536, #PB_Memory_NoClear)
    If *Buffer
      
      ; We wait for answer
      TimeoutCounter = 1000
      Repeat
        Size = ReceiveNetworkData(*WebsocketClient\connection, *Buffer, 65536)
        If Size > 0
          Answer$ + PeekS(*Buffer, Size, #PB_UTF8|#PB_ByteLength)
          Debug Answer$
          If FindString(Answer$, #CRLF$ + #CRLF$)
            Ok = #True
            Break
          EndIf
        Else
          Delay(1)
          TimeoutCounter - 1
        EndIf
      Until Ok Or TimeoutCounter = 0
      
      FreeMemory(*Buffer)
    EndIf
    
    If Ok
      Answer$ = UCase(Answer$)
      
      ; Check answer
      If FindString(Answer$, "HTTP/1.1 101") And FindString(Answer$, "CONNECTION: UPGRADE") And FindString(Answer$, "UPGRADE: WEBSOCKET")
        Result = #True
      EndIf
      
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure.i OpenConnection(*WebsocketClient.WebsocketClient_Structure, URL$)
    
    *WebsocketClient\Protokol$ = GetURLPart(URL$, #PB_URL_Protocol)
    *WebsocketClient\Servername$ = GetURLPart(URL$, #PB_URL_Site)
    
    *WebsocketClient\Port = Val(GetURLPart(URL$, #PB_URL_Port))
    If *WebsocketClient\Port = 0
      If *WebsocketClient\Protokol$ = "wss"
        *WebsocketClient\Port = 443
      Else
        *WebsocketClient\Port = 80
      EndIf
    EndIf
    
    *WebsocketClient\Path$ = GetURLPart(URL$, #PB_URL_Path)
    If *WebsocketClient\Path$ = "" 
      *WebsocketClient\Path$ = "/"
    Else
      If Left(*WebsocketClient\Path$, 1) <> "/"
        *WebsocketClient\Path$ = "/" + *WebsocketClient\Path$
      EndIf
    EndIf
    
    *WebsocketClient\Parameters$ = GetURLPart(URL$, #PB_URL_Parameters)
    If *WebsocketClient\Parameters$ <> ""
      *WebsocketClient\Path$ + "?" + *WebsocketClient\Parameters$
    EndIf
    
    If *WebsocketClient\Protokol$ = "wss" ; If we connect with encryption (https)
      UseNetworkTLS()
      *WebsocketClient\Connection = OpenNetworkConnection(*WebsocketClient\Servername$, *WebsocketClient\Port, #PB_Network_TCP|#PB_Network_TLSv1, 1000)
    ElseIf *WebsocketClient\Protokol$ = "ws"
      *WebsocketClient\Connection = OpenNetworkConnection(*WebsocketClient\Servername$, *WebsocketClient\Port, #PB_Network_TCP, 1000)
    EndIf
    
    If *WebsocketClient\Connection
      If Handshake(*WebsocketClient)
        dbg("Connection and Handshake ok")
      Else
        dbg("Handshake-Error")
        CloseNetworkConnection(*WebsocketClient\Connection)
        *WebsocketClient\Connection = 0
      EndIf
    Else
      dbg("Couldn't connect")
    EndIf
    
    ProcedureReturn *WebsocketClient\Connection
    
  EndProcedure
  
  
  Procedure.i SendFrame(*WebsocketClient.WebsocketClient_Structure, Opcode.i, *Payload, PayloadSize.q, Masked.i=#True)
    
    Protected.i Pos, Size, i, Result, HeaderSize
    Protected.i Dim Mask.a(3)
    Protected *SendBuffer
    
    
    dbg("PayloadSize to send: " + Str(PayloadSize))
    
    ; The Framebuffer, we fill with senddata
    If PayloadSize <= 125
      HeaderSize = 2
    ElseIf PayloadSize >= 126 And PayloadSize <= 65535
      HeaderSize = 4
    Else
      HeaderSize = 10
    EndIf
    
    If Masked
      HeaderSize + 4
    EndIf
    
    dbg("Headersize to send: " + Str(HeaderSize))
    
    *SendBuffer = AllocateMemory(HeaderSize + PayloadSize)
    If *SendBuffer
      
      PokeA(*SendBuffer, Opcode | $80)
      
      Pos = 1 ; The byteposition in the framebuffer
      
      ; Second Byte: Masking(1),length(to 125bytes, else we have to extend)
      If PayloadSize <= 125                                           ; Length fits in first byte
        PokeA(*Sendbuffer + Pos, PayloadSize)               : Pos + 1
      ElseIf PayloadSize >= 126 And PayloadSize <= 65535              ; We have to extend length to third byte
        PokeA(*Sendbuffer + Pos, 126)                       : Pos + 1 ; 126 For 2 extra length bytes
        PokeA(*SendBuffer + Pos, (PayloadSize >> 8))        : Pos + 1 ; First Byte
        PokeA(*SendBuffer + Pos, PayloadSize)               : Pos + 1 ; Second Byte
      Else                                                            ; It's bigger than 65535, we use 8 extra bytes
        PokeA(*Sendbuffer + Pos, 127)                       : Pos + 1 ; 127 for 8 extra length bytes
        PokeA(*SendBuffer + Pos, (PayloadSize >> 56) & $FF) : Pos + 1
        PokeA(*SendBuffer + Pos, (PayloadSize >> 48) & $FF) : Pos + 1
        PokeA(*SendBuffer + Pos, (PayloadSize >> 40) & $FF) : Pos + 1
        PokeA(*SendBuffer + Pos, (PayloadSize >> 32) & $FF) : Pos + 1
        PokeA(*Sendbuffer + Pos, (PayloadSize >> 24) & $FF) : Pos + 1
        PokeA(*Sendbuffer + Pos, (PayloadSize >> 16) & $FF) : Pos + 1
        PokeA(*Sendbuffer + Pos, (PayloadSize >> 8) & $FF)  : Pos + 1
        PokeA(*Sendbuffer + Pos, (PayloadSize & $FF))       : Pos + 1
      EndIf
      
      If Not Masked
        CopyMemory(*Payload, *SendBuffer + Pos, PayloadSize)
      Else
        ; We generate 4 random masking bytes
        
        PokeA(*SendBuffer + 1, PeekA(*SendBuffer + 1) | $80)  ; set mask bit
        
        Mask(0) = Random(255)
        Mask(1) = Random(255) 
        Mask(2) = Random(255) 
        Mask(3) = Random(255) 
        
        ; Write Masking Bytes
        PokeA(*SendBuffer + Pos, Mask(0)) : Pos + 1
        PokeA(*SendBuffer + Pos, Mask(1)) : Pos + 1
        PokeA(*SendBuffer + Pos, Mask(2)) : Pos + 1
        PokeA(*SendBuffer + Pos, Mask(3)) : Pos + 1
        
        CopyMemory(*Payload, *SendBuffer + Pos, PayloadSize)
        
        Size = PayloadSize - 1
        For i = 0 To Size
          PokeA(*SendBuffer + Pos + i, PeekA(*SendBuffer + Pos + i) ! Mask(i % 4))
        Next
      EndIf
      
      ;ShowMemoryViewer(*SendBuffer, MemorySize(*SendBuffer))
      
      If SendNetworkData(*WebsocketClient\connection, *SendBuffer, MemorySize(*SendBuffer)) = MemorySize(*SendBuffer)
        dbg("Frame send, Bytes: " + Str(MemorySize(*SendBuffer)))
        Result = #True
      EndIf
      FreeMemory(*SendBuffer)
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure.i SendPingFrame(*WebsocketClient.WebsocketClient_Structure)
    
    Protected *Payload, PayloadSize.q, Result.i
    
    
    If *WebsocketClient\Connection
      *Payload = UTF8("Ping")
      If *Payload
        PayloadSize = MemorySize(*Payload) - 1
        Result = SendFrame(*WebsocketClient, #Opcode_Ping, *Payload, PayloadSize, #True)
        FreeMemory(*Payload)
      EndIf
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure.i SendTextFrame(*WebsocketClient.WebsocketClient_Structure, Text$, Masked.i=#True)
    
    Protected PayloadSize.q, *Payload, HeaderSize.i, *SendBuffer, Result.i
    
    
    If *WebsocketClient\Connection
      ; Put String in Buffer
      *Payload = UTF8(Text$)
      If *Payload
        PayloadSize = MemorySize(*Payload) - 1
        Result = SendFrame(*WebsocketClient, #Opcode_Text, *Payload, PayloadSize, Masked)
        FreeMemory(*Payload)
      EndIf
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure.i SendBinaryFrame(*WebsocketClient.WebsocketClient_Structure, *Payload, Masked.i=#True)
    
    Protected.i Result, HeaderSize
    Protected.q PayloadSize
    Protected *SendBuffer
    
    
    If *WebsocketClient\Connection
      If *Payload
        PayloadSize = MemorySize(*Payload)
        Result = SendFrame(*WebsocketClient, #Opcode_Binary, *Payload, PayloadSize, Masked)
      EndIf
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure GetPayload(*WebsocketClient.WebsocketClient_Structure, *FrameBuffer, Pos.i)
    
    Protected.i Size, i
    Protected Dim Mask.a(3)
    
    
    *WebsocketClient\ReceiveBuffer = AllocateMemory(*WebsocketClient\frame_size, #PB_Memory_NoClear)
    
    If *WebsocketClient\frame_masking = #False
      CopyMemory(*FrameBuffer + Pos, *WebsocketClient\ReceiveBuffer, *WebsocketClient\frame_size)
    Else
      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
      
      CopyMemory(*FrameBuffer + Pos, *WebsocketClient\ReceiveBuffer, *WebsocketClient\frame_size)
      
      Size = *WebsocketClient\frame_size - 1
      For i = 0 To Size
        PokeA(*WebsocketClient\ReceiveBuffer + i, PeekA(*WebsocketClient\ReceiveBuffer + i) ! Mask(i % 4))
      Next
    EndIf
    
  EndProcedure
  
  
  Procedure.i ReceiveFrame(*WebsocketClient.WebsocketClient_Structure)
    
    Protected.i Size, pos, TimeoutCounter
    Protected.q PayloadSize
    Protected *FrameBuffer, *pongbuffer
    
    
    *FrameBuffer = AllocateMemory(65536, #PB_Memory_NoClear)
    If *FrameBuffer
      
      TimeoutCounter = 1000
      Repeat
        Size = ReceiveNetworkData(*WebsocketClient\connection, *FrameBuffer, MemorySize(*FrameBuffer))
        If Size < 1
          Delay(1)
          TimeoutCounter - 1
        Else
          TimeoutCounter = 1000
        EndIf
      Until Size > -1 Or TimeoutCounter = 0
      
      dbg("Received Frame, Bytes: " + Str(Size))
      
      ;ShowMemoryViewer(*FrameBuffer, Size)
      
      If Size > 0
        
        ; Getting informations about package
        If PeekA(*FrameBuffer) & %10000000 > #False
          ;dbg("Frame not fragmented")
          *WebsocketClient\frame_fragmentation = #False
        Else
          dbg("Frame fragmented! This not supported for now!")
          *WebsocketClient\frame_fragmentation = #True
        EndIf
        
        ; Check masking
        If PeekA(*FrameBuffer + 1) & %10000000 = 128
          *WebsocketClient\frame_masking = #True
        Else
          *WebsocketClient\frame_masking = #False
        EndIf
        dbg("Masking: " + Str(*WebsocketClient\frame_masking))
        
        PayloadSize = PeekA(*FrameBuffer + 1) & $7F
        Pos = 2
        If PayloadSize > 125
          If PayloadSize = 126
            PayloadSize = (PeekA(*FrameBuffer + 2) << 8) | PeekA(*FrameBuffer + 3)
            Pos = 4
          Else
            PayloadSize = (PeekA(*FrameBuffer + 2) << 56) | (PeekA(*FrameBuffer + 3) << 48) | (PeekA(*FrameBuffer + 4) << 40)  | (PeekA(*FrameBuffer + 5) << 32) | (PeekA(*FrameBuffer + 6) << 24) | (PeekA(*FrameBuffer + 7) << 16) | (PeekA(*FrameBuffer + 8) << 8) | PeekA(*FrameBuffer + 9)
            Pos = 10
          EndIf
        EndIf
        *WebsocketClient\frame_size = PayloadSize
        dbg("PayloadSize: " + Str(PayloadSize))
        
        ; Check for Opcodes
        *WebsocketClient\frame_type = PeekA(*FrameBuffer) & $0F
        Select *WebsocketClient\frame_type
          Case #Opcode_Continue
            
          Case #Opcode_Text
            dbg("Text frame")
            GetPayload(*WebsocketClient, *FrameBuffer, Pos)
            
          Case #Opcode_Binary
            dbg("Binary frame")
            GetPayload(*WebsocketClient, *FrameBuffer, Pos)
            
          Case #Opcode_Closing
            dbg("Closing frame")
            If PayloadSize > 1
              *WebsocketClient\Status = PeekA(*FrameBuffer + Pos) << 8
              Pos + 1
              *WebsocketClient\Status | PeekA(*FrameBuffer + Pos)
              Pos + 1
              PayloadSize - 2
              If PayloadSize > 0
                *WebsocketClient\Status$ = PeekS(*FrameBuffer + Pos, PayloadSize, #PB_UTF8|#PB_ByteLength)
              Else
                *WebsocketClient\Status$ = ""
              EndIf
            EndIf
            
            SendCloseFrame(*WebsocketClient)
            CloseNetworkConnection(*WebsocketClient\Connection)
            *WebsocketClient\Connection = 0
            
          Case #Opcode_Ping
            dbg("Ping frame, answere with Pong")
            PokeA(*FrameBuffer + 0, $8A)         ; FIN bit and opcode for pong
            SendNetworkData(*WebsocketClient\Connection, *FrameBuffer, Size)
            
          Case #Opcode_Pong
            dbg("Pong frame")
            GetPayload(*WebsocketClient, *FrameBuffer, Pos)
            
          Default
            dbg("Opcode unknown")
            *WebsocketClient\frame_type = #Opcode_Unknown
            
        EndSelect
        
      EndIf
      
      FreeMemory(*FrameBuffer)
    EndIf
    
    ProcedureReturn *WebsocketClient\frame_type
    
  EndProcedure
  
  
  Procedure.i SendCloseFrame(*WebsocketClient.WebsocketClient_Structure, Status.i=#StatusCode_NormalClosure)
    
    Protected Result.i, *Buffer
    
    
    If *WebsocketClient\Connection
      *Buffer = AllocateMemory(8)
      If *Buffer
        PokeA(*Buffer + 0, $88) ; FIN bit + opcode x8
        PokeA(*Buffer + 1, $82) ; MASK bit + 2 bytes payload
        
        ; MASK bytes
        PokeA(*Buffer + 2, Random(255))
        PokeA(*Buffer + 3, Random(255))
        PokeA(*Buffer + 4, Random(255))
        PokeA(*Buffer + 5, Random(255))
        
        ; masked status in network byte order
        PokeA(*Buffer + 6, (Status >> 8) ! PeekA(*Buffer + 2))
        PokeA(*Buffer + 7, (Status & $FF) ! PeekA(*Buffer + 3))
        
        If SendNetworkData(*WebsocketClient\Connection, *Buffer, MemorySize(*Buffer)) = MemorySize(*Buffer)
          Result = #True
        EndIf
        
        FreeMemory(*Buffer)
      EndIf
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  Procedure CloseConnection(*WebsocketClient.WebsocketClient_Structure, Status.i=#StatusCode_NormalClosure)
    
    If SendCloseFrame(*WebsocketClient, Status)
      ReceiveFrame(*WebsocketClient)
    EndIf
    
    If *WebsocketClient\Connection
      CloseNetworkConnection(*WebsocketClient\Connection)
      *WebsocketClient\Connection = #Null
    EndIf
    
  EndProcedure
  
EndModule
CompilerIf #PB_Compiler_IsMainFile
  ;-Demo
  
  ; Minimal example to send and receive textmessages
  ; The preconfigured testserver "echo.websocket.org" will just echo back everything you've send.
  
  EnableExplicit
  
  
  Enumeration Gadgets
    #String_send
    #String_url
    #Button_connect
    #Frame3D_io
    #Button_send
    #Checkbox_Masked
    #Button_Ping
    #ListView_Output
  EndEnumeration
  
  
  Define.i NetworkEvent, disconnected, event
  Define Send$
  Define WebsocketClient.WebsocketClient::WebsocketClient_Structure
  
  
  OpenWindow(0, 0, 0, 600, 400, "Websocketclient :: Test-GUI", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered)
  
  StringGadget(#String_url, 10, 10, 470, 25, "wss://echo.websocket.org")
  ;StringGadget(#String_url, 10, 10, 470, 25, "wss://doko-cafe.de/test")
  ButtonGadget(#Button_connect, 490, 10, 100, 25, "Connect")
  
  FrameGadget(#Frame3D_io, 10, 40, 580, 350, "Input/Output")
  
  StringGadget(#String_send, 20, 60, 400, 25, "")
  CheckBoxGadget(#Checkbox_Masked, 430, 60, 50, 20, "Mask")
  SetGadgetState(#Checkbox_Masked, #True)
  ButtonGadget(#Button_send, 490, 60, 90, 25, "Send")
  DisableGadget(#Button_send, #True)
  
  ButtonGadget(#Button_Ping, 490, 90, 90, 25, "Ping")
  DisableGadget(#Button_Ping, #True)
  
  ListViewGadget(#ListView_Output, 20, 130, 560, 250)
  
  Repeat
    event = WaitWindowEvent(1)
    Select event
      Case #PB_Event_CloseWindow
        Break
        
      Case #PB_Event_Menu
        Select EventMenu()
        EndSelect
        
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #Button_connect
            If GetGadgetText(#Button_connect) = "Connect"
              Debug "Connect clicked"
              WebsocketClient\Status = 0
              WebsocketClient\Status$ = ""
              If WebsocketClient::OpenConnection(@WebsocketClient, GetGadgetText(#String_url))
                AddGadgetItem(#ListView_Output, -1, "# Connected to " + GetGadgetText(#String_url))
                SetGadgetText(#Button_connect, "Disconnect")
                If GetGadgetText(#String_send) <> ""
                  DisableGadget(#Button_send, #False)
                EndIf
                DisableGadget(#Button_Ping, #False)
              EndIf
            Else
              WebsocketClient::CloseConnection(@WebsocketClient)
              If WebsocketClient\Status > 0
                AddGadgetItem(#ListView_Output, -1, "< Closing status: " + Str(WebsocketClient\Status) + " " + WebsocketClient\Status$)
              EndIf
              SetGadgetText(#Button_connect, "Connect")
              DisableGadget(#Button_send, #True)
              DisableGadget(#Button_Ping, #True)
            EndIf
          Case #String_send
            If EventType() = #PB_EventType_Change
              If GetGadgetText(#String_send) <> ""
                If WebsocketClient\Connection
                  DisableGadget(#Button_send, #False)
                EndIf
              Else
                DisableGadget(#Button_send, #True)
              EndIf
            EndIf
          Case #Button_send
            If Len(GetGadgetText(#String_send)) > 0 And WebsocketClient\connection
              Debug "Send clicked"
              If WebsocketClient::SendTextFrame(@WebsocketClient, GetGadgetText(#String_send), GetGadgetState(#Checkbox_Masked)) = #False
                ;If WebsocketClient::SendTextFrame(@WebsocketClient, "<-" + Space(800) + "->", GetGadgetState(#Checkbox_Masked)) = #False
                Debug "Couldn't send. Are we disconnected?"
              Else
                AddGadgetItem(#ListView_Output, -1, "> " + GetGadgetText(#String_send))
              EndIf
            EndIf
          Case #Button_Ping
            If WebsocketClient::SendPingFrame(@WebsocketClient)
              AddGadgetItem(#ListView_Output, -1, "> Ping sent")
            EndIf
        EndSelect
    EndSelect
    
    
    If WebsocketClient\connection
      
      NetworkEvent = NetworkClientEvent(WebsocketClient\connection)
      
      Select NetworkEvent
          
        Case #PB_NetworkEvent_None
          
        Case #PB_NetworkEvent_Data
          Debug "NetworkEvent_Data"
          WebsocketClient::ReceiveFrame(@WebsocketClient)
          If WebsocketClient\ReceiveBuffer
            If WebsocketClient\frame_type = WebsocketClient::#Opcode_Text
              Debug "< " + PeekS(WebsocketClient\ReceiveBuffer, MemorySize(WebsocketClient\ReceiveBuffer), #PB_UTF8|#PB_ByteLength)
              AddGadgetItem(#ListView_Output, -1, "< " + PeekS(WebsocketClient\ReceiveBuffer, MemorySize(WebsocketClient\ReceiveBuffer), #PB_UTF8|#PB_ByteLength))
            ElseIf WebsocketClient\frame_type = WebsocketClient::#Opcode_Binary
              AddGadgetItem(#ListView_Output, -1, "< Received Binaryframe" )
            ElseIf WebsocketClient\frame_type = WebsocketClient::#Opcode_Pong
              Debug "< " + PeekS(WebsocketClient\ReceiveBuffer, MemorySize(WebsocketClient\ReceiveBuffer), #PB_UTF8|#PB_ByteLength)
              AddGadgetItem(#ListView_Output, -1, "< " + PeekS(WebsocketClient\ReceiveBuffer, MemorySize(WebsocketClient\ReceiveBuffer), #PB_UTF8|#PB_ByteLength))
            EndIf
            FreeMemory(WebsocketClient\ReceiveBuffer)
            WebsocketClient\ReceiveBuffer = #Null
          Else
            If WebsocketClient\frame_type = WebsocketClient::#Opcode_Closing
              SetGadgetText(#Button_connect, "Connect")
              DisableGadget(#Button_send, #True)
              DisableGadget(#Button_Ping, #True)
              AddGadgetItem(#ListView_Output, -1, "< Closing status: " + Str(WebsocketClient\Status) + " " + WebsocketClient\Status$)
            EndIf
          EndIf
          
        Case #PB_NetworkEvent_Disconnect
          If WebsocketClient\Connection
            Debug "NetworkEvent_Disconnect"
            CloseNetworkConnection(WebsocketClient\Connection)
          EndIf
          SetGadgetText(#Button_connect, "Connect")
          DisableGadget(#Button_send, #True)
          DisableGadget(#Button_Ping, #True)
          WebsocketClient\Connection = 0
          
      EndSelect
      
    EndIf
  ForEver
  
CompilerEndIf
wss://demo.piesocket.com/v3/channel_123?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self
wss://echo.