Page 1 of 1

ClientEvent 4 (Server disconnect) include (Win only)

Posted: Sat Jan 22, 2005 7:36 pm
by Lars
Code updated For 5.20+

ClientEvent4_Sample.pb

Code: Select all

;/#################################
;/# ClientEvent 4 include example #
;/#################################
;
;/ Coded by 'Lars'

Procedure ErrorProc(msg.s)
  Debug "Error: " + msg
EndProcedure


If InitNetwork() = 0
  ErrorProc("Could not initialise Network.")
  End
EndIf


XIncludeFile "ClientEvent4.pbi"
CE4_SetErrorProcedure(@ErrorProc()) ;Set our own error procedure


*Mem_Receive = AllocateMemory(1024)
If *Mem_Receive = 0
  ErrorProc("Could not allocate 1KB of memory!")
  End
EndIf


ConnectTo.s = InputRequester("Server adress", "Please enter ServerIP:Port to connect to:", "127.0.0.1:6000")
If ConnectTo = ""
  End
EndIf

ColonPos.l = FindString(ConnectTo, ":", 1)
If ColonPos = 0
  ErrorProc("Invalid server adress Data.")
  End
EndIf

ServerIP.s = Left(ConnectTo, ColonPos - 1)
ServerPort.l = Val(Right(ConnectTo, Len(ConnectTo) - ColonPos))


ConnectionID = OpenNetworkConnection(ServerIP, ServerPort)
If ConnectionID
  CE4_EnableClientEvent4(ConnectionID)
  Repeat
    NetEvent.l = NetworkClientEvent(ConnectionID)
    CE4_CheckClientEvent4(ConnectionID, @NetEvent)
    
    Select NetEvent
      Case 2
        LengthReceived.l = ReceiveNetworkData(ConnectionID, *Mem_Receive, 1023) 
        Debug Str(LengthReceived) + "bytes of Data received!"
        Debug "Data:"
        Debug PeekS(*Mem_Receive, LengthReceived)
      Case 4
        Debug "Server disconnected!"
    EndSelect
  Until NetEvent = 4
Else
  ErrorProc("Could not connect To '" + ServerIP + ":" + Str(ServerPort) + "'.")
  End
EndIf

ClientEvent4.pbi:

Code: Select all

;/#########################
;/# ClientEvent 4 include #
;/#########################
;
;/ Coded by 'Lars'
;
;/ Short how-to:
; To get ClientEvent 4 (server disconnect) responses, first 
; establish a connection, and then enable the ClientEvent4 
; for it with CE4_EnableClientEvent4(). After that, you can
; check for server disconnects with the CE4_CheckClientEvent4()
; procedure.
; When you drop a connection you should disable the server 
; disconnect with the CE4_DisableClientEvent4(), because this 
; procedure deletes the internal linked list element that matches
; hSocket to WSock handles (namely the EventObject handle).
;
;
;/ A few word on error handling:
; On each failure regarding the WSSock2 routines an error procedure
; that you can define (via CE4_SetErrorProcedure()) is called. If 
; you don't specify an own error procedure, which can be used for 
; every thing that could be usefull, you can add log entries or just 
; do nothing.
; In every case you still can call the procedures of the CE4 include, 
; because it has an internal status variable that indicates whether everything
; is ok and thus the procedures just do nothing if this variable indicates
; an error somewhere.
;

Procedure _CE4_Error(msg.s)
  Shared *_CE4_ptrErrorProcedure
  
  If *_CE4_ptrErrorProcedure
    CallFunctionFast(*_CE4_ptrErrorProcedure, @msg)
  EndIf
EndProcedure

;LinkedList that stores the event object for each connection ID
Structure _CE4_EventObjectsStruct
  hSocket.l
  hEventObject.l
EndStructure

NewList _CE4_EventObjects._CE4_EventObjectsStruct()

;Various, not in PB declared constants
#WSA_INVALID_EVENT     = #Null
#WSA_NOT_ENOUGH_MEMORY = #ERROR_NOT_ENOUGH_MEMORY
#WSA_INVALID_HANDLE    = #ERROR_INVALID_HANDLE
#WSA_INVALID_PARAMETER = #ERROR_INVALID_PARAMETER
#WSA_WAIT_EVENT_0      = #Null

;Variable that indicates whether everything is ok. If not, the routines just do nothing, so you don't have to stop the 
;                                                                                                    whole application.
_CE4_StatusOk         = #True
;Internal pointer to yout own Error procedure, set by CE4_SetErrorProcedure(). Standard is 0, which does nothing.
*_CE4_ptrErrorProcedure = 0

;Open the library manually as PB doesn't delcare the WSock2 Functions
_CE4_Library_WSock2 = OpenLibrary(#PB_Any, "ws2_32.dll")
If _CE4_Library_WSock2 = 0
  _CE4_Error("Could not initialise WSock2 API")
  _CE4_StatusOk = #False
EndIf



;CE4_SetErrorProcedure() - Sets the procedure that is called if an error occurs. If you don't use this function, the standard,
;                          which is just doing nothing, is used. Note that the procedure has to have one string parameter which
;                          is an WSock2 error description.
;                          *ptrErrorProcedure.l - pointer to the procedure that is to be executed
;                          Returnvalue: The parameter (useless, but whatever)
Procedure.l CE4_SetErrorProcedure(*ptrErrorProcedure)
  Shared *_CE4_ptrErrorProcedure
  
  *_CE4_ptrErrorProcedure = *ptrErrorProcedure
  
  ProcedureReturn *_CE4_ptrErrorProcedure
EndProcedure


;CE4_EnableClientEvent4() - Enables ClientEvent 4 (server disconnect) for the specified connection hSocket.
;                           hSocket.l is the ConnectionID.
;                           Returnvalue: #True on success, #False on Failure. 
Procedure.l CE4_EnableClientEvent4(hSocket.l)
  Shared _CE4_Library_WSock2, _CE4_StatusOk, _CE4_EventObjects()
  
  _CE4_TempOk = #True
  
  ForEach _CE4_EventObjects()
    If _CE4_EventObjects()\hSocket = hSocket
      ProcedureReturn #False
    EndIf
  Next
  
  If _CE4_StatusOk
    hEventObject.l = CallFunction(_CE4_Library_WSock2, "WSACreateEvent")
    
    If hEventObject = #WSA_INVALID_EVENT
      _CE4_TempOk = #False
      Select WSAGetLastError_()
        Case #WSANOTINITIALISED 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "A successful WSAStartup call must occur before using this function.")
        Case #WSAENETDOWN 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "The network Subsystem has failed.")
        Case #WSAEINPROGRESS 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "A blocking Windows Sockets 1.1 call is in progress, Or The service provider is still processing A callback function.")
        Case #WSA_NOT_ENOUGH_MEMORY
          _CE4_Error("WSock2 Error:" + #CRLF$ + "Not enough free memory available To create The Event object.")
        Default
          _CE4_Error("WSock2 Error.")
      EndSelect
    EndIf
  EndIf
  
  
  If _CE4_StatusOk And _CE4_TempOk
    If CallFunction(_CE4_Library_WSock2, "WSAEventSelect", hSocket, hEventObject, #FD_CLOSE) = #SOCKET_ERROR
      _CE4_TempOk = #False
      Select WSAGetLastError_()
        Case #WSANOTINITIALISED 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "A successful WSAStartup call must occur before using this function.")
        Case #WSAENETDOWN 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "The network Subsystem has failed.")
        Case #WSAEINVAL 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "Indicates that one of The specified parameters was invalid, Or The specified socket is in an invalid State.")
        Case #WSAEINPROGRESS 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "A blocking Windows Sockets 1.1 call is in progress, Or The service provider is still processing A callback function.")
        Case #WSAENOTSOCK 
          _CE4_Error("WSock2 Error:" + #CRLF$ + "The descriptor is not A socket.")
        Default
          _CE4_Error("WSock2 Error.")
      EndSelect
    EndIf
  EndIf
  
  If _CE4_StatusOk And _CE4_TempOk
    AddElement(_CE4_EventObjects())
    _CE4_EventObjects()\hSocket      = hSocket
    _CE4_EventObjects()\hEventObject = hEventObject
    
    ProcedureReturn #True
  EndIf 
  
  ProcedureReturn #False
EndProcedure


;CE4_DisableClientEvent4() - Disables ClientEvent 4 (server disconnect) for the specified connection hSocket and 
;                            deletes the internal linked list element that matches hSocket to WSock handles.
;                            hSocket.l is the ConnectionID.
;                            Returnvalue: #True on success, #False on Failure (no entry with specified hSocket).
Procedure.l CE4_DisableClientEvent4(hSocket.l)
  Shared _CE4_EventObjects()
  ForEach _CE4_EventObjects()
    If _CE4_EventObjects()\hSocket = hSocket
      CloseHandle_(_CE4_EventObjects()\hEventObject)
      DeleteElement(_CE4_EventObjects())
      
      ProcedureReturn #True
    EndIf
  Next
  
  ProcedureReturn #False
EndProcedure


;CE4_CheckClientEvent4() - Checks for a server disconnect event on a connection that has already enabled this event
;                          via CE4_EnableClientEvent4().
;                          hSocket.l is the ConnectionID.
;                          The second parameter is a _pointer_ (!) to your usual netevent variable (long). This procedure 
;                          checks ich this value is 0 and just then returns a server disconnect event is reported the next 
;                          time you call this procedure and netevent is 0. As a result, you can easily check the netevents
;                          and you don't need to implement 2 checks on this.
;                          Returnvalue: #True on success, #False on Failure (no entry with specified hSocket).
Procedure.l CE4_CheckClientEvent4(hSocket.l, *NetEvent.LONG)
  Shared _CE4_Library_WSock2
  
  _CE4_Found.l = #False
  Shared _CE4_EventObjects()
  ForEach _CE4_EventObjects()
    If _CE4_EventObjects()\hSocket = hSocket
      _CE4_Found = #True
      Break
    EndIf
  Next
  
  If _CE4_Found
    If *NetEvent\l = 0
      Select CallFunction(_CE4_Library_WSock2, "WSAWaitForMultipleEvents", 1, @_CE4_EventObjects()\hEventObject, #False, #False, #False)
        Case #WSA_WAIT_EVENT_0
          ResetEvent_(_CE4_EventObjects()\hEventObject)
          *NetEvent\l = 4
        Case #SOCKET_ERROR
          Select WSAGetLastError_()
            Case #WSANOTINITIALISED 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "A successful WSAStartup call must occur before using this function.")
            Case #WSAENETDOWN 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "The network Subsystem has failed.")
            Case #WSAEINPROGRESS 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "A blocking Windows Sockets 1.1 call is in progress, Or The service provider is still processing A callback function.")
            Case #WSA_NOT_ENOUGH_MEMORY 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "Not enough free memory available To complete The operation.")
            Case #WSA_INVALID_HANDLE 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "One Or more of The values in The lphEvents array is Not A valid Event object handle.")
            Case #WSA_INVALID_PARAMETER 
              _CE4_Error("WSock2 Error:" + #CRLF$ + "The cEvents parameter does Not contain A valid handle count.")
            Default
              _CE4_Error("WSock2 Error.")
          EndSelect
      EndSelect
    EndIf
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure
As PB doesn't allow to retrieve Server disconnects as client, I looked for
codes doing that job on the forums and found some.

The problem is, that theese codes work with a WinCallback and thus you
need to have at least an invisible window.

Because I found this quite inelegant I looked for another method to
reveice this event and found it.

This include retrieves server disconnects (kick, connection broken, server
shutting down, whatever) via EventObjects, therefore no Window is
needed.

Because both include and example are together about 250 lines i put it
together in a zip archive:


Download (3KB)


(But nethertheless this is just a workaround, not a compensation for the
feature request :wink: )

Posted: Sat Jan 22, 2005 10:47 pm
by Heis Spiter
Weel done ! I'll use it ;).

Posted: Sat Feb 12, 2005 9:25 pm
by Lars
Update: (V1.01)
Error-Handling has been rewritten, you can use this include in multiple
projects now more easily, because the error-handling-procedure is no
more hard-coded.

Posted: Fri Jun 24, 2005 11:50 am
by Droopy
merci

Posted: Fri Jun 24, 2005 10:17 pm
by Droopy
Works well :D

is it possible to do a Purebasic Library with this code ?