Modul "NetworkManager" v1.0.3 (Windows, Linux, Mac)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Modul "NetworkManager" v1.0.3 (Windows, Linux, Mac)

Beitrag von Sicro »

Dieses Modul ermöglicht das einfache Senden und Empfangen von Strings, Binärdaten und Dateien.

Ich denke, es lassen sich alle erforderlichen Informationen dem Code entnehmen.

Das Modul:

Code: Alles auswählen

; Version: 1.0.3

DeclareModule NetworkManager
 
  EnableExplicit
 
  Enumeration DataType
    #DataType_DataHeader
    #DataType_String
    #DataType_Binary
    #DataType_File
  EndEnumeration
 
  Enumeration EventType
    #Event_DataHeaderReceived           
    #Event_DataHeaderReceivingInProgress
    #Event_DataHeaderReceivingTimeOut
    #Event_BinaryDataReceived
    #Event_BinaryDataReceivingInProgress
    #Event_BinaryDataReceivingTimeOut
    #Event_StringReceived
    #Event_StringReceivingInProgress
    #Event_StringReceivingTimeOut
    #Event_StringReceivingError
    #Event_ClientConnected
    #Event_ClientDisconnected
  EndEnumeration
 
  Declare.s GetDataTypeAsString(DataType.i)
  Declare.i CreateServer(Port.i, IPv6.i=#False, BindedIP.s="")
  Declare   CloseServer(Server.i)
  Declare.i OpenClient(ServerAdress.s, Port.i, IPv6.i=#False, TimeOut.i=60000, LocalIP.s="", LocalPort=0)
  Declare   CloseClient(Client.i)
  Declare.i ServerSendString(Server.i, Client.i, String.s)
  Declare.i ServerSendBinaryData(Server.i, Client.i, *Memory, MemorySize.i)
  Declare.i ServerSendFile(Server.i, Client.i, FilePath.s, FileName.s = "")
  Declare.i ClientSendString(Client.i, String.s)
  Declare.i ClientSendBinaryData(Client.i, *Memory, MemorySize.i)
  Declare.i ClientSendFile(Client.i, FilePath.s, FileName.s = "")
  Declare   ServerReceiveDataHandler(AddresseOfProcedure.i)
  Declare   ClientReceiveDataHandler(AddresseOfProcedure.i)
  Declare   FreeServerClientStringBuffer(Server.i, Client.i)
  Declare   FreeClientStringBuffer(Client.i)
 
EndDeclareModule

Module NetworkManager
   
  ; Interne Einstellungen
  #BufferSize       = 4*1024    ; 4 Kilobyte
  #StringBufferSize = 1024*1024 ; 1 Megabyte
  #SendTimeOut      = 30*1000   ; 30 Sekunden
  #ReceiveTimeOut   = 30*1000   ; 30 Sekunden
 
  InitNetwork()
 
  Structure DataHeader_Struc
    DataSize.q
    FileName.b[1000] ; FileName darf maximal 1000 Bytes lang sein, andernfalls wird abgeschnitten (Datei-Endung bleibt aber erhalten)
    DataType.b       ; muss am Ende der Struktur und ein Byte sein
  EndStructure
 
  Structure Client_Struc
    DataHeader.DataHeader_Struc
    *Buffer
    *StringBuffer ; Speicher wird erst bei String-Empfang allokiert und kann nach dem Empfang wieder freigegeben werden
    CountOfAllReceivedBytes.q
    LastSendTime.i
    LastReceiveTime.i
  EndStructure
 
  Structure Server_Struc
    Map Clients.Client_Struc()
  EndStructure
 
  Prototype ProtoServerCallback(EventServer.i, EventClient.i, Event.i, Param1.i=0, Param2.i=0, Param3.q=0, Param4.q=0, Param5.q=0)
  Prototype ProtoClientCallback(EventClient.i, Event.i, Param1.i=0, Param2.i=0, Param3.q=0, Param4.q=0, Param5.q=0)
 
  Global.Client_Struc NewMap Clients()
  Global.Server_Struc NewMap Servers()
 
  Procedure UpdateLastSendTime(Server.i, Client.i)
   
    If Server
      If FindMapElement(Servers(), Str(Server)) And FindMapElement(Servers()\Clients(), Str(Client))
        Servers()\Clients()\LastSendTime = ElapsedMilliseconds()
      EndIf
    Else
      If FindMapElement(Clients(), Str(Client))
        Clients()\LastSendTime = ElapsedMilliseconds()
      EndIf
    EndIf
   
  EndProcedure
  Procedure.i IsReachedSendTimeOut(Server.i, Client.i)
   
    If Server
      If FindMapElement(Servers(), Str(Server)) And FindMapElement(Servers()\Clients(), Str(Client))
        With Servers()\Clients()
          If ElapsedMilliseconds()-\LastSendTime >= #SendTimeOut
            ProcedureReturn #True
          EndIf
        EndWith
      EndIf
    Else
      If FindMapElement(Clients(), Str(Client)) And ElapsedMilliseconds()-Clients()\LastSendTime >= #SendTimeOut
        ProcedureReturn #True
      EndIf
    EndIf
   
    ProcedureReturn #False
   
  EndProcedure
  Procedure FreeServerClientStringBuffer(Server.i, Client.i)
    
    If FindMapElement(Servers(), Str(Server)) And FindMapElement(Servers()\Clients(), Str(Client))
      With Servers()\Clients()
        If \StringBuffer
          FreeMemory(\StringBuffer)
          \StringBuffer = 0
        EndIf
      EndWith
    EndIf
   
  EndProcedure
  Procedure FreeClientStringBuffer(Client.i)
    
    If FindMapElement(Clients(), Str(Client))
      With Clients()
        If \StringBuffer
          FreeMemory(\StringBuffer)
          \StringBuffer = 0
        EndIf
      EndWith
    EndIf
   
  EndProcedure
  Procedure.s GetDataTypeAsString(DataType.i)
   
    ProcedureReturn StringField("Datenkopf,String,Binärdaten,Datei", DataType+1, ",")
   
  EndProcedure
  Procedure.i CreateServer(Port.i, IPv6.i=#False, BindedIP.s="")
   
    Protected.i Server, Type
   
    Select IPv6
      Case #False : Type = #PB_Network_IPv4
      Case #True  : Type = #PB_Network_IPv6
    EndSelect
   
    Server = CreateNetworkServer(#PB_Any, Port, Type|#PB_Network_TCP, BindedIP)
    If Server = 0 : ProcedureReturn #False : EndIf
   
    If AddMapElement(Servers(), Str(Server))
      ProcedureReturn Server
    Else
      CloseNetworkServer(Server)
      ProcedureReturn #False
    EndIf
   
  EndProcedure
  Procedure CloseServer(Server.i)
  
    If FindMapElement(Servers(), Str(Server))
      With Servers()
        ForEach \Clients()
          FreeMemory(\Clients()\Buffer)
          If \Clients()\StringBuffer
            FreeMemory(\Clients()\StringBuffer)
          EndIf
          CloseNetworkConnection(Val(MapKey(\Clients())))
          DeleteMapElement(\Clients())
        Next
      EndWith
      DeleteMapElement(Servers())
      CloseNetworkServer(Server)
    EndIf
   
  EndProcedure
  Procedure.i OpenClient(ServerAddresse.s, Port.i, IPv6.i=#False, TimeOut.i=60000, LocalIP.s="", LocalPort=0)
   
    Protected.i Client, Type
   
    Select IPv6
      Case #False : Type = #PB_Network_IPv4
      Case #True  : Type = #PB_Network_IPv6
    EndSelect
   
    Client = OpenNetworkConnection(ServerAddresse, Port, Type|#PB_Network_TCP, TimeOut, LocalIP, LocalPort)
    If Client = 0 : ProcedureReturn #False : EndIf
   
    If AddMapElement(Clients(), Str(Client))
      With Clients()
        \DataHeader\DataType = #DataType_DataHeader
        \Buffer = AllocateMemory(#BufferSize, #PB_Memory_NoClear)
        If \Buffer = 0
          CloseNetworkConnection(Client)
          ProcedureReturn #False
        EndIf
      EndWith
    Else
      CloseNetworkConnection(Client)
      ProcedureReturn #False
    EndIf
   
    ProcedureReturn Client
   
  EndProcedure
  Procedure CloseClient(Client.i)
   
    CloseNetworkConnection(Client)
    If FindMapElement(Clients(), Str(Client))
      With Clients()
        FreeMemory(\Buffer)
        If \StringBuffer
          FreeMemory(\StringBuffer)
        EndIf
      EndWith
      DeleteMapElement(Clients())
    EndIf
   
  EndProcedure
  Procedure.i ServerSendString(Server.i, Client.i, String.s)
   
    Protected.DataHeader_Struc *Memory
    Protected.i SentData_Length, SentData_All_Length, StringByteLength
   
    ; String kürzen, wenn das Limit überschritten wird
    Repeat
      StringByteLength = StringByteLength(String, #PB_UTF8)
      If StringByteLength <= #StringBufferSize
        Break
      EndIf
      String = Left(String, Len(String) - 1)
    ForEver
   
    ; Datenkopf und String in einen Speicher schreiben
    *Memory = AllocateMemory(SizeOf(DataHeader_Struc)+StringByteLength)
    If *Memory = 0 : ProcedureReturn #False : EndIf
    *Memory\DataType = #DataType_String
    *Memory\DataSize = StringByteLength
    If PokeS(*Memory+SizeOf(DataHeader_Struc), String, -1, #PB_UTF8|#PB_String_NoZero) <> StringByteLength
      FreeMemory(*Memory)
      ProcedureReturn #False
    EndIf
   
    ; Speicher senden
    UpdateLastSendTime(Server, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, MemorySize(*Memory)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(Server, Client)
      EndIf
      If IsReachedSendTimeOut(Server, Client)
        FreeMemory(*Memory)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)+StringByteLength
   
    FreeMemory(*Memory)
    ProcedureReturn #True
   
  EndProcedure
  Procedure.i ServerSendBinaryData(Server.i, Client.i, *Memory, MemorySize.i)
   
    Protected.DataHeader_Struc DataHeader
    Protected.q SentData_Length, SentData_All_Length
   
    If *Memory = 0 : ProcedureReturn #False : EndIf
   
    ; Datenkopf senden
    DataHeader\DataType = #DataType_Binary
    DataHeader\DataSize = MemorySize
    UpdateLastSendTime(Server, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, @DataHeader+SentData_All_Length, SizeOf(DataHeader_Struc)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(Server, Client)
      EndIf
      If IsReachedSendTimeOut(Server, Client)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)
   
    ; Speicher senden
    SentData_All_Length = 0
    UpdateLastSendTime(Server, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, MemorySize-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(Server, Client)
      EndIf
      If IsReachedSendTimeOut(Server, Client)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = MemorySize
   
    ProcedureReturn #True
   
  EndProcedure
  Procedure.i ServerSendFile(Server.i, Client.i, FilePath.s, FileName.s="")
   
    Protected.i File, StringByteLength
    Protected.q FileLength, ReadedData_Length, SentData_Length, SentData_All_Length
    Protected.s TempFileName
    Protected.DataHeader_Struc DataHeader
    Protected *Memory
   
    *Memory = AllocateMemory(#BufferSize, #PB_Memory_NoClear)
    If *Memory = 0 : ProcedureReturn #False : EndIf
   
    File = ReadFile(#PB_Any, FilePath, #PB_File_SharedRead)
    If File = 0
      FreeMemory(*Memory)
      ProcedureReturn #False
    EndIf
   
    FileLength = Lof(File)
   
    If FileName = ""
      FileName = GetFilePart(FilePath)
    EndIf
   
    ; Dateiname evtl. kürzen, dabei die Datei-Endung bestehen lassen
    Repeat
      StringByteLength = StringByteLength(FileName, #PB_UTF8)
      If StringByteLength <= 1000 ; Dateiname darf nur maximal 1000 Bytes benötigen
        Break
      EndIf
      TempFileName = GetFilePart(FileName, #PB_FileSystem_NoExtension)
      TempFileName = Left(TempFileName, Len(TempFileName)-1)
      If GetExtensionPart(FileName)
        TempFileName + "." + GetExtensionPart(FileName)
      EndIf
      FileName = TempFileName
    ForEver
   
    ; Datenkopf senden
    DataHeader\DataType = #DataType_File
    DataHeader\DataSize = FileLength
    PokeS(@DataHeader\FileName[0], FileName, -1, #PB_UTF8)
    UpdateLastSendTime(Server, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, @DataHeader+SentData_All_Length, SizeOf(DataHeader_Struc)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(Server, Client)
      EndIf
      If IsReachedSendTimeOut(Server, Client)
        FreeMemory(*Memory)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)
   
    ; Datei senden
    While Not Eof(File)
      ReadedData_Length = ReadData(File, *Memory, #BufferSize)
      If ReadedData_Length = 0
        FreeMemory(*Memory)
        CloseFile(File)
        ProcedureReturn #False
      EndIf
     
      SentData_All_Length = 0
      UpdateLastSendTime(Server, Client)
      Repeat
        SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, ReadedData_Length-SentData_All_Length)
        If SentData_Length > 0
          UpdateLastSendTime(Server, Client)
          SentData_All_Length + SentData_Length
        Else
          FreeMemory(*Memory)
          CloseFile(File)
          ProcedureReturn #False
        EndIf
        If IsReachedSendTimeOut(Server, Client)
          FreeMemory(*Memory)
          CloseFile(File)
          ProcedureReturn #False
        EndIf
      Until SentData_All_Length = ReadedData_Length
    Wend
   
    FreeMemory(*Memory)
    CloseFile(File)
    ProcedureReturn #True
   
  EndProcedure
  Procedure.i ClientSendString(Client.i, String.s)
   
    Protected.DataHeader_Struc *Memory
    Protected.i SentData_Length, SentData_All_Length, StringByteLength
   
    ; String kürzen, wenn das Limit überschritten wird
    Repeat
      StringByteLength = StringByteLength(String, #PB_UTF8)
      If StringByteLength <= #StringBufferSize
        Break
      EndIf
      String = Left(String, Len(String) - 1)
    ForEver
   
    ; Datenkopf und String in einen Speicher schreiben
    *Memory = AllocateMemory(SizeOf(DataHeader_Struc)+StringByteLength)
    If *Memory = 0 : ProcedureReturn #False : EndIf
    *Memory\DataType = #DataType_String
    *Memory\DataSize = StringByteLength
    If PokeS(*Memory+SizeOf(DataHeader_Struc), String, -1, #PB_UTF8|#PB_String_NoZero) <> StringByteLength
      FreeMemory(*Memory)
      ProcedureReturn #False
    EndIf
   
    ; Speicher senden
    UpdateLastSendTime(0, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, MemorySize(*Memory)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(0, Client)
      EndIf
      If IsReachedSendTimeOut(0, Client)
        FreeMemory(*Memory)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)+StringByteLength
   
    FreeMemory(*Memory)
    ProcedureReturn #True
   
  EndProcedure
  Procedure.i ClientSendBinaryData(Client.i, *Memory, MemorySize.i)
   
    Protected.DataHeader_Struc DataHeader
    Protected.q SentData_Length, SentData_All_Length
   
    If *Memory = 0 : ProcedureReturn #False : EndIf
   
    ; Datenkopf senden
    DataHeader\DataType = #DataType_Binary
    DataHeader\DataSize = MemorySize
    UpdateLastSendTime(0, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, @DataHeader+SentData_All_Length, SizeOf(DataHeader_Struc)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(0, Client)
      EndIf
      If IsReachedSendTimeOut(0, Client)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)
   
    ; Speicher senden
    SentData_All_Length = 0
    UpdateLastSendTime(0, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, MemorySize-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(0, Client)
      EndIf
      If IsReachedSendTimeOut(0, Client)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = MemorySize
   
    ProcedureReturn #True
   
  EndProcedure
  Procedure.i ClientSendFile(Client.i, FilePath.s, FileName.s="")
   
    Protected.i File, StringByteLength
    Protected.q FileLength, ReadedData_Length, SentData_Length, SentData_All_Length
    Protected.s TempFileName
    Protected.DataHeader_Struc DataHeader
    Protected *Memory
   
    *Memory = AllocateMemory(#BufferSize, #PB_Memory_NoClear)
    If *Memory = 0 : ProcedureReturn #False : EndIf
   
    File = ReadFile(#PB_Any, FilePath, #PB_File_SharedRead)
    If File = 0
      FreeMemory(*Memory)
      ProcedureReturn #False
    EndIf
   
    FileLength = Lof(File)
   
    If FileName = ""
      FileName = GetFilePart(FilePath)
    EndIf
   
    ; Dateiname evtl. kürzen, dabei die Datei-Endung bestehen lassen
    Repeat
      StringByteLength = StringByteLength(FileName, #PB_UTF8)
      If StringByteLength <= 1000 ; Dateiname darf nur maximal 1000 Bytes benötigen
        Break
      EndIf
      TempFileName = GetFilePart(FileName, #PB_FileSystem_NoExtension)
      TempFileName = Left(TempFileName, Len(TempFileName)-1)
      If GetExtensionPart(FileName)
        TempFileName + "." + GetExtensionPart(FileName)
      EndIf
      FileName = TempFileName
    ForEver
   
    ; Datenkopf senden
    DataHeader\DataType = #DataType_File
    DataHeader\DataSize = FileLength
    PokeS(@DataHeader\FileName[0], FileName, -1, #PB_UTF8)
    UpdateLastSendTime(0, Client)
    Repeat
      SentData_Length = SendNetworkData(Client, @DataHeader+SentData_All_Length, SizeOf(DataHeader_Struc)-SentData_All_Length)
      If SentData_Length > 0
        SentData_All_Length + SentData_Length
        UpdateLastSendTime(0, Client)
      EndIf
      If IsReachedSendTimeOut(0, Client)
        FreeMemory(*Memory)
        ProcedureReturn #False
      EndIf
    Until SentData_All_Length = SizeOf(DataHeader_Struc)
   
    ; Datei senden
    While Not Eof(File)
      ReadedData_Length = ReadData(File, *Memory, #BufferSize)
      If ReadedData_Length = 0
        FreeMemory(*Memory)
        CloseFile(File)
        ProcedureReturn #False
      EndIf
     
      SentData_All_Length = 0
      UpdateLastSendTime(0, Client)
      Repeat
        SentData_Length = SendNetworkData(Client, *Memory+SentData_All_Length, ReadedData_Length-SentData_All_Length)
        If SentData_Length > 0
          UpdateLastSendTime(0, Client)
          SentData_All_Length + SentData_Length
        Else
          FreeMemory(*Memory)
          CloseFile(File)
          ProcedureReturn #False
        EndIf
        If IsReachedSendTimeOut(0, Client)
          FreeMemory(*Memory)
          CloseFile(File)
          ProcedureReturn #False
        EndIf
      Until SentData_All_Length = ReadedData_Length
    Wend
   
    FreeMemory(*Memory)
    CloseFile(File)
    ProcedureReturn #True
   
  EndProcedure
  Procedure ServerReceiveDataHandler(Callback.ProtoServerCallback)
   
    Protected.i Event, ReceivedDataSize, BufferOffset
   
    Event = NetworkServerEvent()
    Select Event
      Case #PB_NetworkEvent_None
        If FindMapElement(Servers(), Str(EventServer())) And FindMapElement(Servers()\Clients(), Str(EventClient()))
          With Servers()\Clients()
            If ElapsedMilliseconds()-\LastReceiveTime >= #ReceiveTimeOut
              Select \DataHeader\DataType
                Case #DataType_Binary, #DataType_File
                  Callback(EventServer(), EventClient(), #Event_BinaryDataReceivingTimeOut)
                Case #DataType_String
                  Callback(EventServer(), EventClient(), #Event_StringReceivingTimeOut)
                Case #DataType_DataHeader
                  If \CountOfAllReceivedBytes > 0
                    Callback(EventServer(), EventClient(), #Event_DataHeaderReceivingTimeOut)
                  EndIf
              EndSelect
            EndIf
          EndWith
        EndIf
      Case #PB_NetworkEvent_Data
        If FindMapElement(Servers(), Str(EventServer())) And FindMapElement(Servers()\Clients(), Str(EventClient()))
          With Servers()\Clients()
            ReceivedDataSize = ReceiveNetworkData(EventClient(), \Buffer, #BufferSize)
            If ReceivedDataSize > 0
              BufferOffset = 0
              Repeat
                If \DataHeader\DataType = #DataType_DataHeader
                  \LastReceiveTime = ElapsedMilliseconds()
                  If ReceivedDataSize-BufferOffset >= SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes
                    ; Rest des Datenkopfs ist komplett im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, @\DataHeader+\CountOfAllReceivedBytes, SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes)
                    Callback(EventServer(), EventClient(), #Event_DataHeaderReceived, \DataHeader\DataType, @\DataHeader\FileName, \DataHeader\DataSize)
                    BufferOffset + SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; Datenkopf komplett empfangen, deshalb den Zähler wieder zurücksetzten
                  Else
                    ; Nur ein (weiterer) Teil des Datenkopfs ist im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, @\DataHeader+\CountOfAllReceivedBytes, ReceivedDataSize-BufferOffset)
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(EventServer(), EventClient(), #Event_DataHeaderReceivingInProgress, 0, 0, \CountOfAllReceivedBytes, SizeOf(DataHeader_Struc))
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
               
                If \DataHeader\DataType = #DataType_Binary Or \DataHeader\DataType = #DataType_File
                  \LastReceiveTime = ElapsedMilliseconds()
                  If ReceivedDataSize-BufferOffset >= \DataHeader\DataSize-\CountOfAllReceivedBytes
                    ; Rest der Binär-Daten ist komplett im Speicher vorhanden
                    Callback(EventServer(), EventClient(), #Event_BinaryDataReceived, \DataHeader\DataType, \Buffer+BufferOffset, \DataHeader\DataSize-\CountOfAllReceivedBytes, \DataHeader\DataSize)
                    BufferOffset + \DataHeader\DataSize-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; Binär-Daten komplett empfangen, deshalb den Zähler wieder zurücksetzten
                    \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                  Else
                    ; Nur ein Teil der Binär-Daten ist im Speicher vorhanden
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(EventServer(), EventClient(), #Event_BinaryDataReceivingInProgress, \DataHeader\DataType, \Buffer+BufferOffset, ReceivedDataSize-BufferOffset, \CountOfAllReceivedBytes, \DataHeader\DataSize)
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
               
                If \DataHeader\DataType = #DataType_String
                  \LastReceiveTime = ElapsedMilliseconds()
                  
                  ;TODO: StringBuffer-Overflow-Schutz einbauen
                 
                  If \StringBuffer = 0
                    \StringBuffer = AllocateMemory(#StringBufferSize+1) ; Speicher muss genullt werden (+1 für die abschließende Null)
                    If \StringBuffer = 0
                      \CountOfAllReceivedBytes + \DataHeader\DataSize ; String-Daten überspringen
                      \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                      Callback(EventServer(), EventClient(), #Event_StringReceivingError)
                      Continue
                    EndIf
                  EndIf
                 
                  If ReceivedDataSize-BufferOffset >= \DataHeader\DataSize-\CountOfAllReceivedBytes
                    ; Rest des Strings ist komplett im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, \StringBuffer+\CountOfAllReceivedBytes, \DataHeader\DataSize-\CountOfAllReceivedBytes)
                    Callback(EventServer(), EventClient(), #Event_StringReceived, \StringBuffer, 0, \DataHeader\DataSize)
                    BufferOffset + \DataHeader\DataSize-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; String komplett empfangen, deshalb den Zähler wieder zurücksetzten
                    \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                  Else
                    ; Nur ein Teil des Strings ist im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, \StringBuffer+\CountOfAllReceivedBytes, ReceivedDataSize-BufferOffset)
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(EventServer(), EventClient(), #Event_StringReceivingInProgress, 0, 0, \CountOfAllReceivedBytes, \DataHeader\DataSize)
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
              Until BufferOffset = ReceivedDataSize
            EndIf
          EndWith
        EndIf
      Case #PB_NetworkEvent_Connect
        If FindMapElement(Servers(), Str(EventServer()))
          With Servers()
            If AddMapElement(\Clients(), Str(EventClient()))
              \Clients()\Buffer = AllocateMemory(#BufferSize, #PB_Memory_NoClear)
              If \Clients()\Buffer
                Callback(EventServer(), EventClient(), #Event_ClientConnected)
              Else
                CloseNetworkConnection(EventClient())
                DeleteMapElement(\Clients())
              EndIf
            Else
              CloseNetworkConnection(EventClient())
            EndIf
          EndWith
        EndIf
      Case #PB_NetworkEvent_Disconnect
        ; PureBasic schließt die Verbindung automatisch, kein CloseNetworkConnection() notwendig
        If FindMapElement(Servers(), Str(EventServer())) And FindMapElement(Servers()\Clients(), Str(EventClient()))
          With Servers()\Clients()
            FreeMemory(\Buffer)
            If \StringBuffer
              FreeMemory(\StringBuffer)
            EndIf
            DeleteMapElement(Servers()\Clients())
            Callback(EventServer(), EventClient(), #Event_ClientDisconnected)
          EndWith
        EndIf
    EndSelect
 
  EndProcedure
  Procedure ClientReceiveDataHandler(Callback.ProtoClientCallback)
   
    Protected.i Event, ReceivedDataSize, BufferOffset, Client
   
    ForEach Clients()
      Client = Val(MapKey(Clients()))
      Event = NetworkClientEvent(Client)
      Select Event
        Case #PB_NetworkEvent_None
          With Clients()
            If ElapsedMilliseconds()-\LastReceiveTime >= #ReceiveTimeOut
              Select \DataHeader\DataType
                Case #DataType_Binary, #DataType_File
                  Callback(Client, #Event_BinaryDataReceivingTimeOut)
                  \CountOfAllReceivedBytes = 0
                  \DataHeader\DataType = #DataType_DataHeader
                Case #DataType_String
                  Callback(Client, #Event_StringReceivingTimeOut)
                  \CountOfAllReceivedBytes = 0
                  \DataHeader\DataType = #DataType_DataHeader
                Case #DataType_DataHeader
                  If \CountOfAllReceivedBytes > 0
                    Callback(Client, #Event_DataHeaderReceivingTimeOut)
                    \CountOfAllReceivedBytes = 0
                    \DataHeader\DataType = #DataType_DataHeader
                  EndIf
              EndSelect
            EndIf
          EndWith
        Case #PB_NetworkEvent_Data
          With Clients()
            ReceivedDataSize = ReceiveNetworkData(Client, \Buffer, #BufferSize)
            If ReceivedDataSize > 0
              BufferOffset = 0
              Repeat
                If \DataHeader\DataType = #DataType_DataHeader
                  \LastReceiveTime = ElapsedMilliseconds()
                  If ReceivedDataSize-BufferOffset >= SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes
                    ; Rest des Datenkopfs ist komplett im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, @\DataHeader+\CountOfAllReceivedBytes, SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes)
                    Callback(Client, #Event_DataHeaderReceived, \DataHeader\DataType, @\DataHeader\FileName, \DataHeader\DataSize)
                    BufferOffset + SizeOf(DataHeader_Struc)-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; Datenkopf komplett empfangen, deshalb den Zähler wieder zurücksetzten
                  Else
                    ; Nur ein (weiterer) Teil des Datenkopfs ist im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, @\DataHeader+\CountOfAllReceivedBytes, ReceivedDataSize-BufferOffset)
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(Client, #Event_DataHeaderReceivingInProgress, 0, 0, \CountOfAllReceivedBytes, SizeOf(DataHeader_Struc))
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
                 
                If \DataHeader\DataType = #DataType_Binary Or \DataHeader\DataType = #DataType_File
                  \LastReceiveTime = ElapsedMilliseconds()
                  If ReceivedDataSize-BufferOffset >= \DataHeader\DataSize-\CountOfAllReceivedBytes
                    ; Rest der Binär-Daten ist komplett im Speicher vorhanden
                    Callback(Client, #Event_BinaryDataReceived, \DataHeader\DataType, \Buffer+BufferOffset, \DataHeader\DataSize-\CountOfAllReceivedBytes, \DataHeader\DataSize)
                    BufferOffset + \DataHeader\DataSize-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; Binär-Daten komplett empfangen, deshalb den Zähler wieder zurücksetzten
                    \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                  Else
                    ; Nur ein Teil der Binär-Daten ist im Speicher vorhanden
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(Client, #Event_BinaryDataReceivingInProgress, \DataHeader\DataType, \Buffer+BufferOffset, ReceivedDataSize-BufferOffset, \CountOfAllReceivedBytes, \DataHeader\DataSize)
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
               
                If \DataHeader\DataType = #DataType_String
                  \LastReceiveTime = ElapsedMilliseconds()
                  
                  ;TODO: StringBuffer-Overflow-Schutz einbauen
                 
                  If \StringBuffer = 0
                    \StringBuffer = AllocateMemory(#StringBufferSize+1) ; Speicher muss genullt werden (+1 für die abschließende Null)
                    If \StringBuffer = 0
                      \CountOfAllReceivedBytes + \DataHeader\DataSize ; String-Daten überspringen
                      \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                      Callback(Client, #Event_StringReceivingError)
                    EndIf
                  EndIf
                 
                  If ReceivedDataSize-BufferOffset >= \DataHeader\DataSize-\CountOfAllReceivedBytes
                    ; Rest des Strings ist komplett im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, \StringBuffer+\CountOfAllReceivedBytes, \DataHeader\DataSize-\CountOfAllReceivedBytes)
                    Callback(Client, #Event_StringReceived, \StringBuffer, 0, \DataHeader\DataSize)
                    BufferOffset + \DataHeader\DataSize-\CountOfAllReceivedBytes
                    \CountOfAllReceivedBytes = 0 ; String komplett empfangen, deshalb den Zähler wieder zurücksetzten
                    \DataHeader\DataType = #DataType_DataHeader ; Es wird nun wieder ein Datenkopf erwartet
                  Else
                    ; Nur ein Teil des Strings ist im Speicher vorhanden
                    CopyMemory(\Buffer+BufferOffset, \StringBuffer+\CountOfAllReceivedBytes, ReceivedDataSize-BufferOffset)
                    \CountOfAllReceivedBytes + ReceivedDataSize-BufferOffset
                    Callback(Client, #Event_StringReceivingInProgress, 0, 0, \CountOfAllReceivedBytes, \DataHeader\DataSize)
                    Break ; Keine weiteren Daten vorhanden, daher weitere Verarbeitung überspringen
                  EndIf
                EndIf
              Until BufferOffset = ReceivedDataSize
            EndIf
          EndWith
      EndSelect
    Next
   
  EndProcedure
 
EndModule
Beispiel-Code:

Code: Alles auswählen

EnableExplicit

IncludeFile "NetworkManager.pbi"

Procedure ServerCallback(EventServer.i, EventClient.i, Event.i, Param1.i, Param2.i, Param3.q, Param4.q, Param5.q)
  ;                                                                         Param1 (Integer)           Param2 (Integer)          Param3 (Quad)            Param4 (Quad)            Param5 (Quad)
  ; #Event_DataHeaderReceived            : EventServer, EventClient, Event, DataType,                  AddresseOfFileNameString, DataSize,                0,                       0
  ; #Event_DataHeaderReceivingInProgress : EventServer, EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, HeaderSize,              0
  ; #Event_BinaryDataReceived            : EventServer, EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        DataSize,                0
  ; #Event_BinaryDataReceivingInProgress : EventServer, EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        CountOfAllReceivedBytes, DataSize
  ; #Event_StringReceived                : EventServer, EventClient, Event, StringBufferStartAddresse, 0,                        DataSize,                0,                       0
  ; #Event_StringReceivingInProgress     : EventServer, EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, DataSize,                0
  ; #Event_StringReceivingError          : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
  ; #Event_ClientConnected               : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
  ; #Event_ClientDisconnected            : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
 
  Static.i File
 
  Select Event
    Case NetworkManager::#Event_ClientConnected
      Debug "[Server] Client hat sich verbunden: " + Str(EventClient)
      NetworkManager::ServerSendString(EventServer, EventClient, "Hallo Client "+#DQUOTE$+Str(EventClient)+#DQUOTE$+" !")
    Case NetworkManager::#Event_DataHeaderReceivingInProgress
      Debug "[Server] Datenkopf wird empfangen: " + Str(Param3) + " von " + Str(Param4) + " Bytes empfangen"
    Case NetworkManager::#Event_DataHeaderReceivingTimeOut
      Debug "[Server] Zeitüberschreitung beim Empfang des Datenkopfs"
    Case NetworkManager::#Event_DataHeaderReceived
      If Param1 = NetworkManager::#DataType_File
        Debug "[Server] Datenkopf empfangen (Datentyp: " + NetworkManager::GetDataTypeAsString(Param1) + " -- Datengröße: " + Str(Param3) + " Bytes -- Dateiname: " + PeekS(Param2, -1, #PB_UTF8)
;         File = CreateFile(#PB_Any, GetPathPart(ProgramFilename()) + PeekS(Param2, -1, #PB_UTF8))
      Else
        Debug "[Server] Datenkopf empfangen (Datentyp: " + NetworkManager::GetDataTypeAsString(Param1) + " -- Datengröße: " + Str(Param3) + " Bytes"
      EndIf
    Case NetworkManager::#Event_BinaryDataReceivingInProgress
      Select Param1
        Case NetworkManager::#DataType_Binary
          Debug "[Server] Binärdaten werden empfangen: " + Str(Param4) + " von " + Str(Param5) + " Bytes empfangen"
        Case NetworkManager::#DataType_File
          Debug "[Server] Datei wird empfangen: " + Str(Param4) + " von " + Str(Param5) + " Bytes empfangen"
;           If File
;             WriteData(File, Param2, Param3)
;           EndIf
      EndSelect
    Case NetworkManager::#Event_BinaryDataReceivingTimeOut
      Debug "[Server] Zeitüberschreitung beim Empfang der Binärdaten"
    Case NetworkManager::#Event_BinaryDataReceived
      Select Param1
        Case NetworkManager::#DataType_Binary
          Debug "[Server] Binärdaten wurden empfangen: " + Str(Param4) + " Bytes"
        Case NetworkManager::#DataType_File
          Debug "[Server] Datei wurde empfangen: " + Str(Param4) + " Bytes"
;           If File
;             WriteData(File, Param2, Param3)
;             CloseFile(File)
;             File = 0
;           EndIf
      EndSelect
    Case NetworkManager::#Event_StringReceivingInProgress
      Debug "[Server] String wird empfangen: " + Str(Param3) + " von " + Str(Param4) + " Bytes empfangen"
    Case NetworkManager::#Event_StringReceivingTimeOut
      Debug "[Server] Zeitüberschreitung beim Empfang des Strings"
    Case NetworkManager::#Event_StringReceivingError
      Debug "[Server] Fehler: String konnte nicht empfangen werden"
    Case NetworkManager::#Event_StringReceived
      Debug "[Server] String wurde empfangen: " + PeekS(Param1, -1, #PB_UTF8)
      NetworkManager::FreeServerClientStringBuffer(EventServer, EventClient) ; StringBuffer wieder freigeben, nicht notwendig
    Case NetworkManager::#Event_ClientDisconnected
      Debug "[Server] Client hat sich getrennt: " + Str(EventClient)
  EndSelect
 
EndProcedure
Procedure ClientCallback(EventClient.i, Event.i, Param1.i, Param2.i, Param3.q, Param4.q, Param5.q)
  ;                                                            Param1 (Integer)           Param2 (Integer)          Param3 (Quad)            Param4 (Quad)            Param5 (Quad)
  ; #Event_DataHeaderReceived            : EventClient, Event, DataType,                  AddresseOfFileNameString, DataSize,                0,                       0
  ; #Event_DataHeaderReceivingInProgress : EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, HeaderSize,              0
  ; #Event_BinaryDataReceived            : EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        DataSize,                0
  ; #Event_BinaryDataReceivingInProgress : EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        CountOfAllReceivedBytes, DataSize
  ; #Event_StringReceived                : EventClient, Event, StringBufferStartAddresse, 0,                        DataSize,                0,                       0
  ; #Event_StringReceivingInProgress     : EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, DataSize,                0
  ; #Event_StringReceivingError          : EventClient, Event, 0,                         0,                        0,                       0,                       0
 
  Select Event
    Case NetworkManager::#Event_DataHeaderReceivingInProgress
      Debug "[Client] Datenkopf wird empfangen: " + Str(Param3) + " von " + Str(Param4) + " Bytes empfangen"
    Case NetworkManager::#Event_DataHeaderReceivingTimeOut
      Debug "[Client] Zeitüberschreitung beim Empfang des Datenkopfs"
    Case NetworkManager::#Event_DataHeaderReceived
      If Param1 = NetworkManager::#DataType_File
        Debug "[Client] Datenkopf empfangen (Datentyp: " + NetworkManager::GetDataTypeAsString(Param1) + " -- Datengröße: " + Str(Param3) + " Bytes -- Dateiname: " + PeekS(Param2, -1, #PB_UTF8)
      Else
        Debug "[Client] Datenkopf empfangen (Datentyp: " + NetworkManager::GetDataTypeAsString(Param1) + " -- Datengröße: " + Str(Param3) + " Bytes"
      EndIf
    Case NetworkManager::#Event_BinaryDataReceivingInProgress
      Select Param1
        Case NetworkManager::#DataType_Binary
          Debug "[Client] Binärdaten werden empfangen: " + Str(Param4) + " von " + Str(Param5) + " Bytes empfangen"
        Case NetworkManager::#DataType_File
          Debug "[Client] Datei wird empfangen: " + Str(Param4) + " von " + Str(Param5) + " Bytes empfangen"
      EndSelect
    Case NetworkManager::#Event_BinaryDataReceivingTimeOut
      Debug "[Client] Zeitüberschreitung beim Empfang der Binärdaten"
    Case NetworkManager::#Event_BinaryDataReceived
      Select Param1
        Case NetworkManager::#DataType_Binary
          Debug "[Client] Binärdaten wurden empfangen: " + Str(Param4) + " Bytes"
        Case NetworkManager::#DataType_File
          Debug "[Client] Datei wurde empfangen: " + Str(Param4) + " Bytes"
      EndSelect
    Case NetworkManager::#Event_StringReceivingInProgress
      Debug "[Client] String wird empfangen: " + Str(Param3) + " von " + Str(Param4) + " Bytes empfangen"
    Case NetworkManager::#Event_StringReceivingTimeOut
      Debug "[Client] Zeitüberschreitung beim Empfang des Strings"
    Case NetworkManager::#Event_StringReceivingError
      Debug "[Client] Fehler: String konnte nicht empfangen werden"
    Case NetworkManager::#Event_StringReceived
      Debug "[Client] String wurde empfangen: " + PeekS(Param1, -1, #PB_UTF8)
      NetworkManager::FreeClientStringBuffer(EventClient) ; StringBuffer wieder freigeben, nicht notwendig
  EndSelect
 
EndProcedure

#Port = 6001
Define.i Server1, Server2
Define.i Client1, Client2, Client3
Define.i Event

Server1 = NetworkManager::CreateServer(#Port)
If Not Server1
  Debug "Server konnte nicht gestartet werden!"
  End
EndIf

Server2 = NetworkManager::CreateServer(#Port+1)
If Not Server2
  Debug "Server konnte nicht gestartet werden!"
  NetworkManager::CloseServer(Server1)
  End
EndIf

Client1 = NetworkManager::OpenClient("localhost", #Port)
If Not Client1
  Debug "Client konnte nicht gestartet werden!"
  NetworkManager::CloseServer(Server1)
  NetworkManager::CloseServer(Server2)
  End
EndIf

Client2 = NetworkManager::OpenClient("localhost", #Port)
If Not Client2
  Debug "Client konnte nicht gestartet werden!"
  NetworkManager::CloseClient(client1)
  NetworkManager::CloseServer(Server1)
  NetworkManager::CloseServer(Server2)
  End
EndIf

Client3 = NetworkManager::OpenClient("localhost", #Port+1)
If Not Client3
  Debug "Client konnte nicht gestartet werden!"
  NetworkManager::CloseClient(Client1)
  NetworkManager::CloseClient(Client2)
  NetworkManager::CloseServer(Server1)
  NetworkManager::CloseServer(Server2)
  End
EndIf

Debug "Server und Clients erfolgreich gestartet"

NetworkManager::ClientSendString(Client1, "Hallo Server-1! Hier ist Client-1")
NetworkManager::ClientSendString(Client1, "Hallo Server-1! Hier ist Client-1 -- 2x")
NetworkManager::ClientSendString(Client1, "Hallo Server-1! Hier ist Client-1 -- 3x")
NetworkManager::ClientSendString(Client3, "Hallo Server-2! Hier ist Client-3")
NetworkManager::ClientSendString(Client2, "Hallo Server-1! Hier ist Client-2. Ich sende eine Datei...")
;NetworkManager::ClientSendFile(Client2, "/home/alexander/Schreibtisch/Bild.png")

; Jetzt Strings parallel per Threads senden
; === ACHTUNG: ===========================================================
; = Das parallele Senden funktioniert nur mit unterschiedlichen Clients, =
; = weil die Daten beim Server sonst durcheinander ankommen würden und   =
; = nicht korrekt getrennt werden könnten                                =
; ========================================================================
CompilerIf #PB_Compiler_Thread
  Procedure SendThread(Client.i)
    NetworkManager::ClientSendString(Client, "Client " + Str(Client) + " hat per Thread gesendet")
  EndProcedure
  CreateThread(@SendThread(), Client1) ; Client-1 => Server-1
  CreateThread(@SendThread(), Client2) ; Client-2 => Server-1
  CreateThread(@SendThread(), Client3) ; Client-3 => Server-2
CompilerElse
  Debug "Thread-sicherer Modus ausgeschaltet, Threads-Test wird übersprungen"
CompilerEndIf

OpenWindow(0, 0, 0, 300, 100, "NetworkManager", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
Repeat
  Event = WindowEvent()
  NetworkManager::ServerReceiveDataHandler(@ServerCallback())
  NetworkManager::ClientReceiveDataHandler(@ClientCallback())
Until Event = #PB_Event_CloseWindow

NetworkManager::CloseClient(Client1)
NetworkManager::CloseClient(Client2)
NetworkManager::CloseClient(Client3)
NetworkManager::CloseServer(Server1)
NetworkManager::CloseServer(Server2)
Zuletzt geändert von Sicro am 17.03.2016 18:58, insgesamt 6-mal geändert.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Modul "NetworkManager"

Beitrag von ts-soft »

:allright:
Schöner Code!

Leider 1 Schönheitsfehler:
EnableExplicit wirkt sich auf das Modul aus, wo er deklariert wurde, also im MainSource aber nicht im Module!
Verfrachte also das EnableExplicit in das Modul "NetworkManager", damit es was bewirkt! Das InitNetwork()
solltest Du auch dort plazieren, damit es, falls man das Modul nicht nutzt, nicht aufgerufen wird. Es wird grund-
sätzlich nur einmal aufgerufen, unabhängig ob es im Module oder Main steckt.

PS: Jetzt findest Du auch die fehlende Deklaration für Server :wink:

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Modul "NetworkManager"

Beitrag von Sicro »

Danke ts-soft :)

EnableExplicit() ist jetzt auch im Modul (DeclareModule).

Dass das mehrfache Ausführen von InitNetwork() inzwischen keine Probleme mehr macht, ist mir entfallen und hatte ich deshalb nicht im Modul.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Modul "NetworkManager" (Windows, Linux, Mac)

Beitrag von Sicro »

Noch ein Beispiel. Hier kann der Server den Clients ein Bild senden und diese zeigen dieses dann an.

Server:

Code: Alles auswählen

IncludeFile "NetworkManager.pbi"

EnableExplicit

#Window_Main = 0
#Button_Send = 0

Global NewMap Clients.i()
Define.i Server, Event
Define.s ImageFilePath

Procedure ServerCallback(EventServer.i, EventClient.i, Event.i, Param1.i, Param2.i, Param3.q, Param4.q, Param5.q)
  ;                                                                         Param1 (Integer)           Param2 (Integer)          Param3 (Quad)            Param4 (Quad)            Param5 (Quad)
  ; #Event_DataHeaderReceived            : EventServer, EventClient, Event, DataType,                  AddresseOfFileNameString, DataSize,                0,                       0
  ; #Event_DataHeaderReceivingInProgress : EventServer, EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, HeaderSize,              0
  ; #Event_BinaryDataReceived            : EventServer, EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        DataSize,                0
  ; #Event_BinaryDataReceivingInProgress : EventServer, EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        CountOfAllReceivedBytes, DataSize
  ; #Event_StringReceived                : EventServer, EventClient, Event, StringBufferStartAddresse, 0,                        DataSize,                0,                       0
  ; #Event_StringReceivingInProgress     : EventServer, EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, DataSize,                0
  ; #Event_StringReceivingError          : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
  ; #Event_ClientConnected               : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
  ; #Event_ClientDisconnected            : EventServer, EventClient, Event, 0,                         0,                        0,                       0,                       0
 
  Select Event
    Case NetworkManager::#Event_ClientConnected
      Debug "Client hat sich verbunden: " + Str(EventClient)
      AddMapElement(Clients(), Str(EventClient))
    Case NetworkManager::#Event_ClientDisconnected
      Debug "Client hat sich getrennt: " + Str(EventClient)
      DeleteMapElement(Clients(), Str(EventClient))
  EndSelect
 
EndProcedure

If Not OpenWindow(#Window_Main, #PB_Ignore, #PB_Ignore, 200, 200, "Server", #PB_Window_SystemMenu)
  Debug "Fenster konnte nicht erstellt werden!"
  End
EndIf

ButtonGadget(#Button_Send, 50, 35, 100 , 30, "Senden")

Server = NetworkManager::CreateServer(6000)
If Not Server
  MessageRequester("Server", "Server konnte nicht gestartet werden!")
  End
EndIf

Repeat
  Event = WaitWindowEvent(1)
  NetworkManager::ServerReceiveDataHandler(@ServerCallback())
  If Event = #PB_Event_Gadget And EventGadget() = #Button_Send
    ImageFilePath = OpenFileRequester("Bild senden", "", "", 0)
    If ImageFilePath <> ""
      ; Bild an alle Clients senden
      ForEach Clients()
        Debug "Send..."
        NetworkManager::ServerSendFile(Server, Val(MapKey(Clients())), ImageFilePath)
        Debug "Sent"
      Next
    EndIf
  EndIf
Until Event = #PB_Event_CloseWindow

NetworkManager::CloseServer(Server)
Client:

Code: Alles auswählen

IncludeFile "NetworkManager.pbi"

UseJPEG2000ImageDecoder()
UseJPEGImageDecoder()
UsePNGImageDecoder()

EnableExplicit

#Window_Main = 0
#ImageGadget = 0

Define.i Event, Client

Procedure ClientCallback(EventClient.i, Event.i, Param1.i, Param2.i, Param3.q, Param4.q, Param5.q)
  ;                                                            Param1 (Integer)           Param2 (Integer)          Param3 (Quad)            Param4 (Quad)            Param5 (Quad)
  ; #Event_DataHeaderReceived            : EventClient, Event, DataType,                  AddresseOfFileNameString, DataSize,                0,                       0
  ; #Event_DataHeaderReceivingInProgress : EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, HeaderSize,              0
  ; #Event_BinaryDataReceived            : EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        DataSize,                0
  ; #Event_BinaryDataReceivingInProgress : EventClient, Event, DataType,                  BufferStartAddresse,      BufferDataLength,        CountOfAllReceivedBytes, DataSize
  ; #Event_StringReceived                : EventClient, Event, StringBufferStartAddresse, 0,                        DataSize,                0,                       0
  ; #Event_StringReceivingInProgress     : EventClient, Event, 0,                         0,                        CountOfAllReceivedBytes, DataSize,                0
  ; #Event_StringReceivingError          : EventClient, Event, 0,                         0,                        0,                       0,                       0
 
  Static *ImageMemory, ImageMemoryOffset.i
 
  Select Event
    Case NetworkManager::#Event_BinaryDataReceivingInProgress
      Debug "Datei wird empfangen: " + Str(Param4) + " von " + Str(Param5) + " Bytes empfangen"
      If *ImageMemory = 0
        *ImageMemory = AllocateMemory(Param5)
      EndIf
      CopyMemory(Param2, *ImageMemory+ImageMemoryOffset, Param3)
      ImageMemoryOffset + Param3
    Case NetworkManager::#Event_BinaryDataReceivingTimeOut
      Debug "Zeitüberschreitung beim Empfang der Binärdaten"
      If *ImageMemory
        FreeMemory(*ImageMemory)
        *ImageMemory = 0
        ImageMemoryOffset = 0
      EndIf
    Case NetworkManager::#Event_BinaryDataReceived
      Debug "Binärdaten wurden empfangen: " + Str(Param4) + " Bytes"
      If *ImageMemory = 0
        *ImageMemory = AllocateMemory(Param5)
      EndIf
      CopyMemory(Param2, *ImageMemory+ImageMemoryOffset, Param3)
      If CatchImage(0, *ImageMemory, Param4)
        ResizeImage(0, GadgetWidth(#ImageGadget), GadgetHeight(#ImageGadget)) ; Bild verkleinern, wenn es zu groß ist.
        SetGadgetState(0, ImageID(0))
      Else
        Debug "Bild konnte nicht aus dem Speicher geladen werden!"
      EndIf
      FreeMemory(*ImageMemory)
      *ImageMemory = 0
      ImageMemoryOffset = 0
  EndSelect
 
EndProcedure

If Not OpenWindow(#Window_Main, #PB_Ignore, #PB_Ignore, 400, 400, "Client", #PB_Window_SystemMenu)
  Debug "Fenster konnte nicht erstellt werden!"
  End
EndIf

ImageGadget(#ImageGadget, 5, 5, WindowWidth(#Window_Main)-5, WindowHeight(#Window_Main)-5, 0)

Client = NetworkManager::OpenClient("localhost", 6000)
If Not Client
  MessageRequester("Client", "Keine Verbindung zum Server möglich!")
  End
EndIf

Repeat
  Event = WaitWindowEvent(1)
  NetworkManager::ClientReceiveDataHandler(@ClientCallback())
Until Event = #PB_Event_CloseWindow

NetworkManager::CloseClient(Client)
Zuletzt geändert von Sicro am 31.10.2015 19:58, insgesamt 2-mal geändert.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: Modul "NetworkManager" (Windows, Linux, Mac)

Beitrag von Bisonte »

Dazu mal ein paar Verständnisfragen....

In der Hilfe steht bei Send und ReceiveNetworkData():
On UDP connections, the maximum 'Length' is 2048. On TCP connections, the maximum 'Length' is 65536.
Also dürfte doch bei der Sendeprozedur der Buffer nie grösser als z.B. 65536 Bytes bei TCP sein, oder irre ich mich da ?
Wenn ich also mit deiner Methode Files mit mehr als 64kb verschicken würde, müsste PB da nicht Fehler melden ?

Nach meinem Verständnis nimmst Du als Bufferlänge den Gesamten Speicher, den es zu übertragen gilt minus dem schon gesendeten... oder ?

Und noch eine andere Frage die mich beschäftigt :

Wenn es nun jemand geschafft hat sich mit meinem erstellten Server zu verbinden, und mir sagen wir mal ein riesengrosses File sendet, was ich aber gar nicht
annehmen möchte, muss ich da den Download trotzdem bis zum Ende durchziehen und den Speicher immer verwerfen, um den Empfangsbuffer zu leeren,
oder reicht es aus den Client nur zu "kicken" und dann ist nur das bis dahin gesendete im Empfangsbuffer, was man leeren muss....
(Eher eine Verständnisfrage, wie PB oder das jeweilige Betriebssystem handhabt, hat jetzt nichts mit deinem Modul selbst zu tun)
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Modul "NetworkManager" (Windows, Linux, Mac)

Beitrag von Sicro »

Bisonte hat geschrieben:In der Hilfe steht bei Send und ReceiveNetworkData():
On UDP connections, the maximum 'Length' is 2048. On TCP connections, the maximum 'Length' is 65536.
Also dürfte doch bei der Sendeprozedur der Buffer nie grösser als z.B. 65536 Bytes bei TCP sein, oder irre ich mich da ?
Wenn ich also mit deiner Methode Files mit mehr als 64kb verschicken würde, müsste PB da nicht Fehler melden ?
Ja, der bessere Weg wäre es sicherlich, sich strikt an die Angaben der Hilfe zu halten.
Vielleicht sind diese Angaben falsch, weil es funktionieren auch höhere Werte, mit denen mehr Geschwindigkeit erzielt werden kann (weniger Schleifen-Durchläufe).

Server

Code: Alles auswählen

If Not InitNetwork() Or Not CreateNetworkServer(0, 6500)
  Debug "Network error"
  End
EndIf

#BufferSize = 1024*1024 ; 1 MiB, also mehr als 65.536 Bytes
*Memory = AllocateMemory(#BufferSize)

Repeat
  Event = NetworkServerEvent()
  Select Event
    Case #PB_NetworkEvent_Connect
      Debug "Client has connected."
    Case #PB_NetworkEvent_Data
      Debug ReceiveNetworkData(EventClient(), *Memory, #BufferSize)
    Default
      Delay(10)
  EndSelect
ForEver
Client

Code: Alles auswählen

 If Not InitNetwork()
  Debug "Network error"
  End
EndIf

Connection = OpenNetworkConnection("localhost", 6500)
If Not Connection
  Debug "Network error"
  End
EndIf

#FileSize = 1024*1024*10 ; 10 MiB

*FileData = AllocateMemory(#FileSize)
Offset = 0
Repeat
  SentDataSize = SendNetworkData(Connection, *FileData+Offset, #FileSize-Offset)
  If SentDataSize > 0
    Offset + SentDataSize
  EndIf
Until Offset = #FileSize
Bisonte hat geschrieben:Wenn es nun jemand geschafft hat sich mit meinem erstellten Server zu verbinden, und mir sagen wir mal ein riesengrosses File sendet, was ich aber gar nicht
annehmen möchte, muss ich da den Download trotzdem bis zum Ende durchziehen und den Speicher immer verwerfen, um den Empfangsbuffer zu leeren,
oder reicht es aus den Client nur zu "kicken" und dann ist nur das bis dahin gesendete im Empfangsbuffer, was man leeren muss....
Zweiter Fall ist richtig, erster Fall jedoch auch, wenn du den Client nicht "kicken" willst.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Modul "NetworkManager" v1.0.2 (Windows, Linux, Mac)

Beitrag von Sicro »

Fehler im Modul behoben:
  • ServerSendString() und ClientSendString haben #StringBufferSize nicht berücksichtigt, sondern immer nur 1000 Bytes gesendet
  • Wenn sich ein Client vom Server getrennt hat, wurde er nicht wirklich aus der Map entfernt, wodurch CloseServer() dann beim FreeMemory() ein Fehler auslöste.
Fehler im Client-Code vom zweiten Beispiel behoben:
  • ImageMemory wurde mehrfach verwendet und nie vergrößert => kleines Bild => großes Bild => Memory-Overflow
  • ImageMemoryOffset wurde nicht immer zurückgesetzt
Leichtsinnsfehler - da war ich wohl zu müde :coderselixir:
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Antworten