Aus langeweile habe ich gestern mal wieder ein wenig mit Client Server Geschichten rumgespielt. Die Kommunikation zwischen Client und Server scheint soweit auch zu funktionieren, jedoch sollen nun auch größere Dateien (1GB) per TCP zwischen beiden übertragen werden.
Dabei gibt es zwei Probleme in meinem Script.
1. Die Dateigröße der übertragenen Datei weicht vom Original ab. Ich vermute das passiert, beim letzten eingelesenen Part der Datei, aber ich habe keine wirkliche Idee wie ich das beheben kann.
2. Der Transfer ist verdammt langsam, was für einen Transfer übers Internet zwar noch akzeptabel wäre, aber solange ich client und server auf dem selben Computer betreibe sollten doch eigentlich deutlich höhere Transferraten möglich sein. Ich erreiche mit den aktuellen Scripten lediglich 4-5 MB/s, verzehnfache ich die Größe der Dateiparts im Server und den zugehörigen Lesepuffer im Client, dann erreiche ich immerhin 11 mb/s, was im Vergleich zum normalen Kopieren weniger als ein zehntel der maximalen Geschwindigkeit ist. Ist die Größe der Parts jedoch zu hoch reagiert der Server nicht mehr richtig, weil er relativ lange mit dem Senden eines einzelnen Parts beschäftigt ist, das muss auf jedenfall vermieden werden. Auch sollen später langsame Clients (Internet) nicht ausgebremst werden, weil sie Möglicherweise mehrere Sekunden für einen Dateipart benötigen, bevor sie wieder andere Dinge wie GUI cmds abarbeiten können. Die Partgröße zu erhöhen fällt also schonmal weg um die Performance zu verbessern.
Vielleicht hat ja jemand von euch Ideen wie man die Performance erhöhen könnte und den Fehler bei der Übertragung behebt.
Anmerkungen zu den Scripten:
Der Server sollte per Scite ausgeführt werden um alle Debug Meldungen aus der Console lesen zu können.
Der Client sollte kompiliert werden.
Um einen Dateitransfer zu starten muss zuerst der Server, dann der Client gestartet werden, im client dann den connect Button nutzen und im Anschluss den getfile Button.
Vor dem ersten Testlauf muss im Server noch die globale Variable $testfile angepasst werden, hier sollte eine Datei mit mindestens 500 MB verwendet werden.
Client
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
TCPStartUp()
[/autoit] [autoit][/autoit] [autoit]Global $IP = "127.0.0.1"
Global $socket = -1
Global $datasocket = -1
Global $peerAliveTimer = TimerInit()
Global $maxlen = 512000 ; tcprecv buffer
Global $delim = "$##$"
Global $alivecounter = 0
Global $filesize = -1
Global $retrievedData = 0
Global $filename = "unknown.dat"
Global $filehandle = -1
#Region ### START Koda GUI section ### Form=
$guimain = GUICreate("Client Window", 444, 385, 192, 124)
$editconsole = GUICtrlCreateEdit("", 24, 104, 369, 249)
$btngetfile = GUICtrlCreateButton("getfile", 24, 0, 60, 24)
$btnsend = GUICtrlCreateButton("send", 290, 30, 60, 40, $WS_GROUP)
$btnconnect = GUICtrlCreateButton("connect", 370, 30, 60, 40, $WS_GROUP)
$Inputmsg = GUICtrlCreateInput("", 24, 30, 250, 20)
$inputNick = GUICtrlCreateInput("Nick", 24, 60, 250, 20)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###
While 1
if $socket <> -1 Then
$data = TCPRecv($socket,$maxlen)
if $data <> "" Then
$aTCPmsg=StringSplit($data,$delim,1)
For $x=1 To $aTCPMsg[0]
If $aTCPMsg[$x]="" Then ContinueLoop
Switch $aTCPMsg[$x]
Case "~!~server forced disconnect~!~"
GUICtrlSetData($editConsole,"INFO received by server: " & "Sitzung vom Server beendet (kick)" & @CRLF & GUICtrlRead($editConsole))
TCPCloseSocket($socket)
$socket = -1
GUICtrlSetState($btnconnect,$GUI_ENABLE)
Case "~!~server shutdown~!~"
GUICtrlSetData($editConsole,"INFO received by server: " & "Sitzung vom Server beendet (shutdown)" & @CRLF & GUICtrlRead($editConsole))
TCPCloseSocket($socket)
$socket = -1
GUICtrlSetState($btnconnect,$GUI_ENABLE)
Case "~!~Nick registered~!~"
GUICtrlSetData($editConsole,"INFO received by server: " & "Nickname wurde vom Server registriert" & @CRLF & GUICtrlRead($editConsole))
Case "~!~Nick changed~!~"
GUICtrlSetData($editConsole,"INFO received by server: " & "Nickname wurde vom Server geändert" & @CRLF & GUICtrlRead($editConsole))
Case Else
GUICtrlSetData($editConsole,"MSG received by server: " & $aTCPMsg[$x] & @CRLF & GUICtrlRead($editConsole))
EndSwitch
Next
EndIf
If TimerDiff($peerAliveTimer) > 10000 Then
$peerAliveTimer = TimerInit()
$alivecounter+=1
$alive = sendmessage($socket,"~!~alive~!~")
;WinSetTitle($guimain,"",$alivecounter)
if $alive = 0 Then
GUICtrlSetData($editConsole,"TCP Connection lost: " & $socket & @CRLF & GUICtrlRead($editConsole))
TCPCloseSocket($socket)
$socket = -1
GUICtrlSetState($btnconnect,$GUI_ENABLE)
EndIf
EndIf
EndIf
if $datasocket <> -1 And $filehandle <> -1 Then
$alive = sendmessage($datasocket,"~!~alive~!~")
if $alive = 0 Then
TCPCloseSocket($datasocket)
FileClose($filehandle)
$datasocket=-1
$filehandle=-1
Else
$binarydata = TCPRecv($datasocket,1024000,1)
FileWrite($filehandle,$binarydata)
EndIf
EndIf
$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
_terminateClient()
Exit
Case $btnsend
if $socket = -1 Then
MsgBox(0,"Client Fehler","Server not connected.")
Else
sendmessage($socket,GUICtrlRead($Inputmsg))
GUICtrlSetData($editConsole,"Data send to server: " & GUICtrlRead($Inputmsg) & @CRLF & GUICtrlRead($editConsole))
GUICtrlSetData($Inputmsg,"")
EndIf
Case $btnconnect
if $socket = -1 Then
$socket = TCPConnect( $IP, 5555)
If $socket = -1 Then
MsgBox(0,"Client-Fehler","Verbindung zum Server fehlgeschlagen")
Else
$peerAliveTimer=TimerInit()
GUICtrlSetState($btnconnect,$GUI_DISABLE)
sendmessage($socket,"~!~IDENT~!~" & guictrlread($inputNick))
EndIf
Else
MsgBox(0,"Client Fehler","Already connected to server")
EndIf
Case $btngetfile
$datasocket=TCPConnect($ip,5555)
sendmessage($datasocket,"~!~getFile~!~")
if Not FileExists(@ScriptDir & "\downloads") Then DirCreate(@ScriptDir & "\downloads")
$filehandle = FileOpen(@ScriptDir & "\downloads\" & $filename,17)
;$filehandle = FileOpen("F:\" & $filename,17)
EndSwitch
WEnd
[/autoit] [autoit][/autoit] [autoit]Func _terminateClient()
sendmessage($socket,"~!~disconnect~!~")
TCPCloseSocket($socket)
TCPShutdown()
EndFunc
Func sendmessage($socket,$msg)
Return TCPSend($socket,$msg & $delim)
EndFunc
Server
#include <array.au3>
#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
TCPStartUp()
[/autoit] [autoit][/autoit] [autoit]Global $ServerIP = "127.0.0.1"
Global $MainSocket = TCPListen($ServerIP, 5555)
If $MainSocket = -1 Then Exit MsgBox(0,"Server-Fehler","Listen Socket konnte nicht erstellt werden")
Global $aPeers[1]=[0]
Global $aDataTransfers[1]=[0]
Global $peercount = 0
Global $maxLen = 512000 ; tcprecv buffer
Global $peerAliveTimer = TimerInit()
Global $totalTimer = TimerInit()
Global $aNickNames[1][2]=[["Socket","Nick"]]
Global $delim = "$##$"
Global $testfile = "S:\test.dat" ; <---- ÄNDERN !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#Region ### START Koda GUI section ### Form=
$guiServer = GUICreate("Server Control", 364, 414, 1328, 195)
$labelmain = GUICtrlCreateLabel("Server Control Window", 48, 72, 113, 17)
$labelpeers = GUICtrlCreateLabel("Connected Peers:", 48, 104, 89, 17)
$labelpeerscount = GUICtrlCreateLabel($peercount, 192, 104, 30, 17)
$labelNickName = GUICtrlCreateLabel("", 100, 170, 100, 17)
$peercombo = GUICtrlCreateCombo("", 48, 200, 153, 25)
$labelPeerList = GUICtrlCreateLabel("Peerlist", 48, 168, 38, 17)
$btndiscpeer = GUICtrlCreateButton("dissconnect peer", 240, 192, 97, 41, $WS_GROUP)
$btndiscallpeers = GUICtrlCreateButton("dissconnect all", 239, 246, 97, 41, $WS_GROUP)
$btnsend = GUICtrlCreateButton("send", 210, 340, 50, 40, $WS_GROUP)
$btnsendall = GUICtrlCreateButton("send all", 270, 340, 50, 40, $WS_GROUP)
$inputmsg = GUICtrlCreateInput("", 40, 350, 150, 20)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###
While 1
[/autoit] [autoit][/autoit] [autoit]$newPeer = TCPAccept($MainSocket)
if $newPeer <> -1 Then
_ArrayAdd($aPeers,$newPeer)
$aPeers[0] += 1
ConsoleWrite("New TCP Connection established: " & $newPeer & @CRLF)
fillPeerCombo()
EndIf
if $aPeers[0] > 0 Then
$resetAliveTimer = True
For $i=1 to $aPeers[0]
; check socket alive
If TimerDiff($peerAliveTimer) > 10000 Then
$alive = sendmessage($aPeers[$i],"~!~alive~!~")
if $alive = 0 Then
ConsoleWrite("TCP Connection lost: " & $aPeers[$i] & @CRLF)
$resetAliveTimer = False
removePeer($i)
ExitLoop
EndIf
EndIf
; check new data
$newdata = TCPRecv($aPeers[$i],$maxLen)
If $newdata <> "" Then
;ConsoleWrite("rawData: " & $newdata & @CRLF)
$aTCPMsg=StringSplit($newdata,$delim,1)
;_ArrayDisplay($aTCPMsg)
For $x=1 To $aTCPMsg[0] ; msg kann mehrere teilnachrichten enthalten
If $aTCPMsg[$x]="" Then ContinueLoop
Switch $aTCPMsg[$x]
Case "~!~disconnect~!~"
ConsoleWrite("INFO by client (" & $aPeers[$i] & ") received: Session disconnected" & @CRLF)
removePeer($i)
ExitLoop 2
Case "~!~alive~!~"
; ignore it
Case "~!~getFile~!~"
_ArrayAdd($aDataTransfers,$aPeers[$i])
Global $transtimer = TimerInit()
$filehandle = FileOpen($testfile,16)
$aDataTransfers[0]+=1
Case Else
if StringInStr($aTCPMsg[$x],"~!~IDENT~!~",1) Then
$nick = StringReplace($aTCPMsg[$x],"~!~IDENT~!~","")
ConsoleWrite("INFO by client (" & $aPeers[$i] & ") received: Identified with nick (" & $nick & ")" & @CRLF)
$temp = _ArraySearch($aNickNames,$aPeers[$i])
If $temp = -1 Then
ReDim $aNickNames[UBound($aNickNames)+1][2]
$aNickNames[UBound($aNickNames)-1][0]=$aPeers[$i]
$aNickNames[UBound($aNickNames)-1][1]=$nick
sendmessage($aPeers[$i],"~!~Nick registered~!~")
Else
$aNickNames[$temp][0]=$aPeers[$i]
$aNickNames[$temp][1]=$nick
sendmessage($aPeers[$i],"~!~Nick changed~!~")
EndIf
GUICtrlSetData($labelNickName,$nick)
Else
ConsoleWrite("MSG by client (" & $aPeers[$i] & ") received: " & $aTCPMsg[$x] & @CRLF)
EndIf
EndSwitch
Next
EndIf
Next
If TimerDiff($peerAliveTimer) > 10000 And $resetAliveTimer = True Then $peerAliveTimer = TimerInit()
EndIf
If $peercount <> $aPeers[0] Then
$peercount = $aPeers[0]
GUICtrlSetData($labelpeerscount,$peercount)
EndIf
If $aDataTransfers[0] > 0 Then
For $i=1 To $aDataTransfers[0]
$datapacket=FileRead($filehandle,1024000)
If @extended > 0 Then TCPSend($aDataTransfers[$i],$datapacket)
If @error=-1 Then
TCPCloseSocket($aDataTransfers[$i])
_ArrayDelete($aDataTransfers,$i)
$aDataTransfers[0]-=1
$size = Round(FileGetSize($testfile)/1024/1024,0)
$time = Round(TimerDiff($transtimer)/1000,0)
if @Compiled Then
MsgBox(0,"file transfer complete","FILE TRANSFER Complete: " & $size & " MB" & @TAB & $time & "s" & @TAB & Round($size/$time,2) & "MB/s")
Else
ConsoleWrite("FILE TRANSFER Complete: " & $size & " MB" & @TAB & $time & "s" & @TAB & Round($size/$time,2) & "MB/s" & @CRLF)
EndIf
ExitLoop
EndIf
Next
EndIf
[/autoit] [autoit][/autoit] [autoit]$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
_terminateserver()
Exit
Case $btndiscpeer
$socket = GUICtrlRead($peercombo)
sendmessage($socket,"~!~server forced disconnect~!~")
ConsoleWrite("Manual peer disconnect: " & GUICtrlRead($peercombo) & @CRLF)
removePeer(_ArraySearch($aPeers,$socket))
Case $btndiscallpeers
ConsoleWrite("Manual peer disconnect All: " & @CRLF)
For $i=1 to $aPeers[0]
sendmessage($aPeers[$i],"~!~server forced disconnect~!~")
TCPCloseSocket($aPeers[$i])
Next
Global $temp[1]=[0]
$aPeers=$temp
fillPeerCombo()
Case $btnsend
$socket = GUICtrlRead($peercombo)
if $socket <> "" Then
sendmessage($socket,GUICtrlRead($inputmsg))
Else
ConsoleWrite("FATALERROR: no peer selected: " & @CRLF)
EndIf
Case $btnsendall
For $i=1 To $aPeers[0]
sendmessage($aPeers[$i],GUICtrlRead($inputmsg))
Next
EndSwitch
WEnd
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func removePeer($peerID)
if $peerID = -1 Or $peerID = 0 Then Return ConsoleWrite("FATALERROR: peer not found in array: " & $peerID & @CRLF)
TCPCloseSocket($aPeers[$peerID])
_ArrayDelete($aPeers,$peerID)
$aPeers[0] -= 1
fillPeerCombo()
EndFunc
Func fillPeerCombo()
Local $temp = ""
For $i=1 to $aPeers[0]
$temp &= $aPeers[$i] & "|"
Next
GUICtrlSetData($peercombo,"")
if $aPeers[0] <> 0 Then GUICtrlSetData($peercombo,StringTrimRight($temp,1),$aPeers[$aPeers[0]])
EndFunc
Func _terminateserver()
For $i=1 to $aPeers[0]
sendmessage($aPeers[$i],"~!~server shutdown~!~")
TCPCloseSocket($aPeers[$i])
ConsoleWrite("TCP Connection disconnected by server: " & $aPeers[$i] & @CRLF)
Next
TCPCloseSocket($MainSocket)
ConsoleWrite("Server socket closed: " & $MainSocket & @CRLF)
TCPShutdown()
EndFunc
Func sendmessage($socket,$msg)
Return TCPSend($socket,$msg & $delim)
EndFunc