SafeNetwork.pbi (PB Netzwerk-Befehlserweiterung)
Verfasst: 03.11.2010 12:43
Servus.
Das folgende Include ist bei der Entwicklung von EProxy entstanden, ist aber meiner Meinung nach inzwischen umfangreich genug für eine eigene Veröffentlichung.
Das SafeNetwork.pbi ...
SafeNetwork.pbi
Das folgende Include ist bei der Entwicklung von EProxy entstanden, ist aber meiner Meinung nach inzwischen umfangreich genug für eine eigene Veröffentlichung.
Das SafeNetwork.pbi ...
- Erweitert die Das NetworkClientEvent() um das #PB_NetworkEvent_Disconnect
- Macht sämtliche Netzwerkbefehle Threadsicher
- Macht sämtliche Netzwerkbefehle 'kugelsicher' (ungültige Handles führen nicht zum Absturz)
- Optional, per CompilerSwitch: Wrappt die Verbindungshandles von SpeicherAdressen auf fortlaufende Nummern, nützlich für die Verwaltung
SafeNetwork.pbi
Code: Alles auswählen
; ------------------------------------------------------------------------------------
; Sichert die PB - Netzwerkbefehle ab
; ------------------------------------------------------------------------------------
; Client #PB_Network_Event_Disconnect support by DarkPlayer, PureFan
; http://www.purebasic.fr/english/viewtopic.php?f=12&t=42559
; Muss aktiviert werden falls UDP verwendet werden soll
#SafeNetwork_UDPWorkAround = #False
CompilerIf #SafeNetwork_UDPWorkAround = #True
; NICHT ÄNDERN! Muss für den UDP-WA abgeschaltet sein!
#SafeNetwork_UseVId = #False
CompilerElse
; Abstrahiert die Connection - Handes. (1,2,3,4, ... statt Speicheradressen, werden nicht wiederverwendet. Nützlich für Threads)
#SafeNetwork_UseVId = #False
CompilerEndIf
;{ Client - #PB_NetworkEvent_Disconnect - Erkennung
CompilerIf #PB_Compiler_OS = #PB_OS_Linux ;{
#FIONREAD = $541B
#__FD_SETSIZE = 1024
#__NFDBITS = 8 * SizeOf(LONG)
Macro __FDELT(d)
((d) / #__NFDBITS)
EndMacro
Macro __FDMASK(d)
(1 << ((d) % #__NFDBITS))
EndMacro
Structure FD_SET
fds_bits.l[#__FD_SETSIZE / #__NFDBITS]
EndStructure
Procedure.i __FD_SET(d.i, *set.FD_SET)
If d >= 0 And d < #__FD_SETSIZE
*set\fds_bits[__FDELT(d)] | __FDMASK(d)
EndIf
EndProcedure
Procedure.i __FD_ISSET(d.i, *set.FD_SET)
If d >= 0 And d < #__FD_SETSIZE
ProcedureReturn *set\fds_bits[__FDELT(d)] & __FDMASK(d)
EndIf
EndProcedure
Procedure.i __FD_ZERO(*set.FD_SET)
FillMemory(*set, SizeOf(FD_SET), 0, #PB_Byte)
EndProcedure
#FD_SETSIZE = #__FD_SETSIZE
#NFDBITS = #__NFDBITS
Macro FD_SET(fd, fdsetp)
__FD_SET(fd, fdsetp)
EndMacro
Macro FD_ISSET(fd, fdsetp)
__FD_ISSET(fd, fdsetp)
EndMacro
Macro FD_ZERO(fdsetp)
__FD_ZERO(fdsetp)
EndMacro
; Returns the minimum value for NFDS
Procedure.i _NFDS(*set.FD_SET)
Protected I.i, J.i
For I = SizeOf(FD_SET)/SizeOf(LONG) - 1 To 0 Step -1
If *set\fds_bits[I]
For J = (#__NFDBITS - 1) To 0 Step -1
If *set\fds_bits[I] & (1 << J)
ProcedureReturn I * #__NFDBITS + J + 1
EndIf
Next
EndIf
Next
ProcedureReturn 0
EndProcedure
;}
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS ;{
#IOC_OUT = $40000000 ;(__uint32_t)
Macro _IOR(g,n,t)
_IOC(#IOC_OUT, (g), (n), (t))
EndMacro
#IOCPARM_MASK = $1fff
Macro _IOC(inout,group,num,len)
((inout) | (((len) & #IOCPARM_MASK) << 16) | ((group) << 8) | (num))
EndMacro
#FIONREAD = _IOR('f', 127, SizeOf(LONG))
#__DARWIN_FD_SETSIZE = 1024
#__DARWIN_NBBY = 8
#__DARWIN_NFDBITS = SizeOf(LONG) * #__DARWIN_NBBY
Structure FD_SET
fds_bits.l[ (#__DARWIN_FD_SETSIZE + #__DARWIN_NFDBITS - 1) / #__DARWIN_NFDBITS ]
EndStructure
Procedure.i __DARWIN_FD_SET(fd.i, *p.FD_SET)
If fd >= 0 And fd < #__DARWIN_FD_SETSIZE
*p\fds_bits[fd / #__DARWIN_NFDBITS] | (1 << (fd % #__DARWIN_NFDBITS))
EndIf
EndProcedure
Procedure.i __DARWIN_FD_ISSET(fd.i, *p.FD_SET)
If fd >= 0 And fd < #__DARWIN_FD_SETSIZE
ProcedureReturn *p\fds_bits[fd / #__DARWIN_NFDBITS] & (1 << (fd % #__DARWIN_NFDBITS))
EndIf
EndProcedure
Procedure.i __DARWIN_FD_ZERO(*p.FD_SET)
FillMemory(*p, SizeOf(FD_SET), 0, #PB_Byte)
EndProcedure
#FD_SETSIZE = #__DARWIN_FD_SETSIZE
Macro FD_SET(n, p)
__DARWIN_FD_SET(n, p)
EndMacro
Macro FD_ISSET(n, p)
__DARWIN_FD_ISSET(n, p)
EndMacro
Macro FD_ZERO(p)
__DARWIN_FD_ZERO(p)
EndMacro
; Returns the minimum value for NFDS
Procedure.i _NFDS(*p.FD_SET)
Protected I.i, J.i
For I = SizeOf(FD_SET)/SizeOf(LONG) - 1 To 0 Step -1
If *p\fds_bits[I]
For J = (#__DARWIN_NFDBITS - 1) To 0 Step -1
If *p\fds_bits[I] & (1 << J)
ProcedureReturn I * #__DARWIN_NFDBITS + J + 1
EndIf
Next
EndIf
Next
ProcedureReturn 0
EndProcedure
;}
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Windows ;{
; #FIONREAD is already defined
; FD_SET is already defined
Structure _fd_set
fd_count.i
fd_array.i[#FD_SETSIZE]
EndStructure
Macro fd_set
_fd_set
EndMacro
Macro FD_ZERO(set)
set\fd_count = 0
EndMacro
Procedure.i FD_SET(fd.i, *set.FD_SET)
If *set\fd_count < #FD_SETSIZE
*set\fd_array[ *set\fd_count ] = fd
*set\fd_count + 1
EndIf
EndProcedure
Procedure.i FD_ISSET(fd.i, *set.FD_SET)
Protected I.i
For I = *set\fd_count - 1 To 0 Step -1
If I < #FD_SETSIZE
If *set\fd_array[I] = fd
ProcedureReturn #True
EndIf
EndIf
Next
ProcedureReturn #False
EndProcedure
Procedure.i _NFDS(*set.FD_SET)
ProcedureReturn *set\fd_count
EndProcedure
;}
CompilerEndIf
CompilerIf Defined(TIMEVAL, #PB_Structure) = #False ;{
Structure TIMEVAL
tv_sec.l
tv_usec.l
EndStructure ;}
CompilerEndIf
;}
Structure __ConnectionInfo
Mutex.i
ConId.i
EndStructure
Structure __vId_Holder
Id.q
exist.a
EndStructure
Structure __NetworkProtector_Data
Map Connections.__ConnectionInfo()
CompilerIf #SafeNetwork_UseVId
Map vIdHolder.__vId_Holder()
Map vIdRevHolder.__vId_Holder()
vIdMutex.i
vId.i
CompilerEndIf
CompilerIf #SafeNetwork_UDPWorkAround
waUDPMutex.i
CompilerEndIf
Mutex.i
ECMutex.i
EventClient.i
EndStructure
Global __NetworkProtector.__NetworkProtector_Data
; vId - Manager abstrahiert sämtliche Connection - Ids
; Neue vId anlegen
Procedure __vId_Add(Connection)
Shared __NetworkProtector
Protected vId
CompilerIf #SafeNetwork_UseVId
With __NetworkProtector
If Connection
LockMutex(\vIdMutex)
If FindMapElement(\vIdRevHolder(), Str(Connection))
DeleteMapElement(\vIdHolder(), Str(\vIdRevHolder(Str(Connection))\Id)) ; Alten Holder löschen
DeleteMapElement(\vIdRevHolder(), Str(Connection)) ; Alten RevHolder löschen
EndIf
\vId + 1
; Überschlag
If \vId <= 0
\vId = 1
EndIf
vId = \vId
\vIdHolder(Str(vId))\Id = Connection
\vIdRevHolder(Str(Connection))\Id = vId
\vIdHolder()\exist = #True
\vIdRevHolder()\exist = #True
UnlockMutex(\vIdMutex)
EndIf
EndWith
CompilerElse
vId = Connection
CompilerEndIf
ProcedureReturn vId
EndProcedure
; vId in Connection - Id umwandeln
Procedure __vId_Get(vId)
Shared __NetworkProtector
Protected Connection
CompilerIf #SafeNetwork_UseVId
With __NetworkProtector
LockMutex(\vIdMutex)
If FindMapElement(\vIdHolder(), Str(vId))
Connection = \vIdHolder()\Id
EndIf
UnlockMutex(\vIdMutex)
EndWith
CompilerElse
Connection = vId
CompilerEndIf
ProcedureReturn Connection
EndProcedure
; Connection in vId umwandeln
Procedure __vId_RevGet(Connection)
Shared __NetworkProtector
Protected vId
CompilerIf #SafeNetwork_UseVId
With __NetworkProtector
LockMutex(\vIdMutex)
If FindMapElement(\vIdRevHolder(), Str(Connection))
vId = \vIdRevHolder()\Id
EndIf
UnlockMutex(\vIdMutex)
EndWith
CompilerElse
vId = Connection
CompilerEndIf
ProcedureReturn vId
EndProcedure
; vId austragen
Procedure __vId_Remove(vId)
Shared __NetworkProtector
Protected Connection, tvId
CompilerIf #SafeNetwork_UseVId
With __NetworkProtector
LockMutex(\vIdMutex)
Connection = \vIdHolder(Str(vId))
tvId = \vIdRevHolder(Str(Connection))
DeleteMapElement(\vIdHolder(), Str(vId))
If tvId = vId
DeleteMapElement(\vIdRevHolder(), Str(Connection))
EndIf
UnlockMutex(\vIdMutex)
EndWith
CompilerEndIf
EndProcedure
; Mutex sicher freigeben
Macro _SafeFreeMutex(_Mutex)
; Letzten Lock - Zyklus abwarten
LockMutex(_Mutex) : UnlockMutex(_Mutex)
LockMutex(_Mutex) : UnlockMutex(_Mutex)
FreeMutex(_Mutex) ; Mutex freigeben
EndMacro
; Erstellt die Verwaltung
Procedure __NetworkProtector_Init()
Shared __NetworkProtector
With __NetworkProtector
\Mutex = CreateMutex() ; Globale Schutzmutex für die Verwaltung
\ECMutex = CreateMutex() ; Globale Schutzmutex für den EventClient
CompilerIf #SafeNetwork_UDPWorkAround
\waUDPMutex = CreateMutex() ; Workaround für UDP
CompilerEndIf
CompilerIf #SafeNetwork_UseVId
\vIdMutex = CreateMutex() ; Globale Schutzmutex für die Virtuellen Ids
CompilerEndIf
EndWith
EndProcedure : __NetworkProtector_Init()
; Event - Client abstrahieren
Procedure __EventClient()
Shared __NetworkProtector
With __NetworkProtector
ProcedureReturn \EventClient
EndWith
EndProcedure
Procedure __NetworkServerEvent()
Protected Event, Mutex
Shared __NetworkProtector
With __NetworkProtector
LockMutex(\ECMutex)
Event = NetworkServerEvent()
UnlockMutex(\ECMutex)
If Event
If Event = #PB_NetworkEvent_Connect ; Connect - Event : Lokale Schutzmutex für jede Verbindung erzeugen
LockMutex(\ECMutex)
\EventClient = __vId_Add(EventClient())
UnlockMutex(\ECMutex)
LockMutex(\Mutex)
; Mutex erstellen
If Not \Connections(Str(\EventClient))\Mutex
\Connections()\Mutex = CreateMutex()
\Connections()\ConId = ConnectionID(__vId_Get(\EventClient))
EndIf
UnlockMutex(\Mutex)
ElseIf Event = #PB_NetworkEvent_Disconnect ; Disconnect - Event: Lokale Schutzmutex abbauen
LockMutex(\ECMutex)
\EventClient = __vId_RevGet(EventClient())
UnlockMutex(\ECMutex)
If \EventClient
LockMutex(\Mutex)
Mutex = \Connections(Str(\EventClient))\Mutex
If Mutex
; Mutex austragen
\Connections()\Mutex = #Null
DeleteMapElement(\Connections(), Str(\EventClient))
; virtuell freigeben
__vId_Remove(\EventClient)
; Freigeben
_SafeFreeMutex(Mutex)
EndIf
UnlockMutex(\Mutex)
Else
Event = #Null
EndIf
Else
LockMutex(\ECMutex)
\EventClient = __vId_RevGet(EventClient())
UnlockMutex(\ECMutex)
EndIf
EndIf
EndWith
ProcedureReturn Event
EndProcedure
Procedure __NetworkClientEvent(Connection)
Protected Event, Mutex, rConnection
Protected hSocket.i, tv.timeval, readfds.fd_set, Length.i
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
hSocket = \Connections()\ConId
UnlockMutex(\Mutex)
If Mutex And rConnection
LockMutex(Mutex)
If hSocket < 65536 And hSocket > 0
Event = NetworkClientEvent(rConnection)
Else
Event = #PB_NetworkEvent_Disconnect
EndIf
If Not Event ; Erweiterung um das #PB_NetworkEvent_Disconnect zu erkennen
tv\tv_sec = 0 ; Dont even wait, just query status
tv\tv_usec = 0
If hSocket > 65536 Or hSocket < 1 ; Gültiger bereich
Event = #PB_NetworkEvent_Disconnect
Else
FD_ZERO(readfds)
FD_SET(hSocket, readfds)
; Check if there is something new
Event = select_(_NFDS(readfds), @readfds, #Null, #Null, @tv)
If Event < 0 ; Seems to be an error
Event = #PB_NetworkEvent_Disconnect
ElseIf Event = 0 Or Not FD_ISSET(hSocket, readfds) ; No data available
Event = #Null
Else
; Check if data is available?
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Event = ioctlsocket_(hSocket, #FIONREAD, @Length)
CompilerElse
Event = ioctl_(hSocket, #FIONREAD, @Length)
CompilerEndIf
If Event Or Length = 0 ; Not successful to query for data available OR no data available ? This seems to be an error!
Event = #PB_NetworkEvent_Disconnect
Else
Event = #Null
EndIf
EndIf
EndIf
EndIf
UnlockMutex(Mutex)
If Event = #PB_NetworkEvent_Disconnect ; Disconnect - Event: Mutex austragen
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
If Mutex
; Mutex Austragen
\Connections()\Mutex = #Null
DeleteMapElement(\Connections(), Str(Connection))
__vId_Remove(Connection)
; Freigeben
_SafeFreeMutex(Mutex)
EndIf
UnlockMutex(\Mutex)
EndIf
Else
Event = #PB_NetworkEvent_Disconnect
EndIf
EndWith
ProcedureReturn Event
EndProcedure
Procedure __OpenNetworkConnection(Host$, Port, Type = #PB_Network_TCP)
Protected Connection
Shared __NetworkProtector
With __NetworkProtector
Connection = OpenNetworkConnection(Host$, Port, Type)
If Connection
Connection = __vId_Add(Connection)
LockMutex(\Mutex)
\Connections(Str(Connection))\Mutex = CreateMutex()
\Connections()\ConId = ConnectionID(__vId_Get(Connection))
UnlockMutex(\Mutex)
EndIf
EndWith
ProcedureReturn Connection
EndProcedure
Procedure __CloseNetworkConnection(Connection)
Protected isOk, Mutex, rConnection
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
If rConnection
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
UnlockMutex(\Mutex)
If Mutex
; Connection beenden
LockMutex(Mutex)
isOk = CloseNetworkConnection(rConnection)
UnlockMutex(Mutex)
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
If Mutex
\Connections()\Mutex = #Null
DeleteMapElement(\Connections(), Str(Connection))
__vId_Remove(Connection)
; Freigeben
_SafeFreeMutex(Mutex)
EndIf
UnlockMutex(\Mutex)
EndIf
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
Procedure __ConnectionID(Connection)
Protected isOk, Mutex
Shared __NetworkProtector
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
isOk = \Connections()\ConId
UnlockMutex(\Mutex)
If Not Mutex
isOk = #Null
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
Procedure __SendNetworkData(Connection, *Mem, MemSize)
Protected isOk, Mutex, rConnection
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
UnlockMutex(\Mutex)
CompilerIf #SafeNetwork_UDPWorkAround
If Not Mutex
Mutex = \waUDPMutex
EndIf
CompilerEndIf
If Mutex And rConnection
LockMutex(Mutex)
isOk = SendNetworkData(rConnection, *Mem, MemSize)
UnlockMutex(Mutex)
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
Procedure __SendNetworkString(Connection, String$)
Protected isOk, Mutex, rConnection
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
UnlockMutex(\Mutex)
CompilerIf #SafeNetwork_UDPWorkAround
If Not Mutex
Mutex = \waUDPMutex
EndIf
CompilerEndIf
If Mutex And rConnection
LockMutex(Mutex)
isOk = SendNetworkString(rConnection, String$)
UnlockMutex(Mutex)
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
Procedure __SendNetworkFile(Connection, File$)
Protected isOk, Mutex, rConnection
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
UnlockMutex(\Mutex)
CompilerIf #SafeNetwork_UDPWorkAround
If Not Mutex
Mutex = \waUDPMutex
EndIf
CompilerEndIf
If Mutex And rConnection
LockMutex(Mutex)
isOk = SendNetworkFile(rConnection, File$)
UnlockMutex(Mutex)
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
Procedure __ReceiveNetworkData(Connection, *Mem, MemSize)
Protected isOk, Mutex, rConnection
Shared __NetworkProtector
rConnection = __vId_Get(Connection)
With __NetworkProtector
LockMutex(\Mutex)
Mutex = \Connections(Str(Connection))\Mutex
UnlockMutex(\Mutex)
CompilerIf #SafeNetwork_UDPWorkAround
If Not Mutex
Mutex = \waUDPMutex
EndIf
CompilerEndIf
If Mutex And rConnection
LockMutex(Mutex)
isOk = ReceiveNetworkData(rConnection, *Mem, MemSize)
UnlockMutex(Mutex)
EndIf
EndWith
ProcedureReturn isOk
EndProcedure
; PB - Befehle überschreiben
Macro EventClient()
__EventClient()
EndMacro
Macro NetworkServerEvent()
__NetworkServerEvent()
EndMacro
Macro NetworkClientEvent(Connection)
__NetworkClientEvent(Connection)
EndMacro
Macro OpenNetworkConnection(ServerName, Port, Type = #PB_Network_TCP)
__OpenNetworkConnection(ServerName, Port, Type)
EndMacro
Macro CloseNetworkConnection(Connection)
__CloseNetworkConnection(Connection)
EndMacro
Macro ConnectionID(Connection)
__ConnectionID(Connection)
EndMacro
Macro SendNetworkData(Connection, Mem, MemSize)
__SendNetworkData(Connection, Mem, MemSize)
EndMacro
Macro SendNetworkString(Connection, String)
__SendNetworkString(Connection, String)
EndMacro
Macro SendNetworkFile(Connection, File)
__SendNetworkFile(Connection, File)
EndMacro
Macro ReceiveNetworkData(Connection, Mem, MemSize)
__ReceiveNetworkData(Connection, Mem, MemSize)
EndMacro