Jump to content
caleb41610

Multiple connection TCP server

Recommended Posts

caleb41610

This is the base for most of my programs that require TCP. I've completely rewritten it with optimizations and proper packet handling, which can be added to easily.

* Completely rewrote the script 11/04/12

* Greatly optimized speed. 1000 connections in 4.02 seconds on my machine.

* Added a vastly configurable packet system.

* GUI changed slightly. (Still a minimalist example GUI)

* Raw message packet example added

* PNG image transfer packet example added

* Username / Computername listview information packet example added.

* WM_NOTIFY menu removed

TBA features:

* Packet checksum error checking

* More examples

* Client-to-client communication through the server

* Better GUI

GDIPlus and Crypt start up but are not used. I have plans to use them eventually so they were left in the script. Remove them if you want.

I plan to add a check for pending connections and process them immediately instead of only processing one connection then continuing the main loop. Then you could theoretically have a lot more speed than you would with the current method of processing new connections.

Multi-connection server:

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_Res_Comment=Multi Client Server
#AutoIt3Wrapper_Res_Fileversion=1.0.0.2
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
#AutoIt3Wrapper_Res_requestedExecutionLevel=requireAdministrator
#AutoIt3Wrapper_Run_Tidy=y
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****


#include <GUIConstantsEx.au3>
#include <Array.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>
#include <Crypt.au3>
#include <GDIPlus.au3>
#include <Base64.au3>
TCPStartup() ; Starts up TCP
_Crypt_Startup() ; Starts up Crypt
_GDIPlus_Startup() ; Starts up GDIPlus

#region ;**** Declares you can edit ****
Opt("TrayIconDebug", 1)
Global $BindIP = "0.0.0.0" ; IP Address to listen on. 0.0.0.0 = all available.
Global $BindPort = "1337" ; Port to listen on.
Global $MaxConnections = 1000 ; Maximum number of allowed connections.
Global $PacketSize = 1000 ; Maximum size to receive when packets are checked.
#endregion ;**** Declares you can edit ****

#region ;**** Declares that shouldn't be edited ****
OnAutoItExitRegister("_ServerClose")
Opt("TCPTimeout", 0)
Opt("GUIOnEventMode", 1)
Global $WS2_32 = DllOpen("Ws2_32.dll") ; Opens Ws2_32.dll to be used later.
Global $NTDLL = DllOpen("ntdll.dll") ; Opens NTDll.dll to be used later.
Global $TotalConnections = 0 ; Holds total connection number.
Global $SocketListen = -1 ; Variable for TCPListen()
Global $Connection[$MaxConnections + 1][11] ; Array to connection information.
Global $SocketListen
Global $PacketEND = "[PACKET_END]" ; Defines the end of a packet
Global $PacketMSG = "[PACKET_TYPE_0001]" ; Plain text message
Global $PacketPNG = "[PACKET_TYPE_0002]" ; Base64 of PNG binary.
Global $PacketPCI = "[PACKET_TYPE_0003]" ; UserName@PC name
#endregion ;**** Declares that shouldn't be edited ****

#region ;**** GUI ****
Global $GUI = GUICreate("Server List", 300, 400)
Global $ServerList = GUICtrlCreateListView("#|Socket|IP|User|Computer", 5, 5, 290, 360)
Global $ServerMenu = GUICtrlCreateMenu("Server")
Global $ServerStartListen = GUICtrlCreateMenuItem("On", $ServerMenu, 2, 1)
Global $ServerStopListen = GUICtrlCreateMenuItem("Off", $ServerMenu, 3, 1)
Global $ConnectionMenu = GUICtrlCreateMenu("Connection")
Global $ConnectionKill = GUICtrlCreateMenuItem("Close", $ConnectionMenu, 1)
GUICtrlCreateMenuItem("", $ConnectionMenu, 2)
Global $ConnectionKillAll = GUICtrlCreateMenuItem("Close All", $ConnectionMenu, 3)
GUISetOnEvent($GUI_EVENT_CLOSE, "_ServerClose")
GUICtrlSetOnEvent($ServerStartListen, "_ServerListenStart")
GUICtrlSetOnEvent($ServerStopListen, "_ServerListenStop")
GUICtrlSetOnEvent($ConnectionKill, "_ConnectionKill")
GUICtrlSetOnEvent($ConnectionKillAll, "_ConnectionKillAll")
GUISetState(@SW_SHOW, $GUI)
#endregion ;**** GUI ****

_Main() ; Starts the main function

Func _Main()
While 1
_CheckNewConnections()
_CheckNewPackets()
_Sleep(1000, $NTDLL)
WEnd
EndFunc ;==>_Main

Func _CheckNewConnections()
Local $SocketAccept = TCPAccept($SocketListen) ; Tries to accept a new connection.
If $SocketAccept = -1 Then ; If we found no new connections,
Return ; skip the rest and return to _Main().
EndIf

If $TotalConnections >= $MaxConnections Then ; If we reached the maximum connections allowed,
TCPSend($SocketAccept, "MAXIMUM_CONNECTIONS_REACHED") ; tell the connecting client that we cannot accept the connection,
TCPCloseSocket($SocketAccept) ; close the socket,
Return ; skip the rest and return to _Main().
EndIf
; Since we got this far, we must have a new connection.
$TotalConnections += 1 ; Add to the total connections.
$Connection[$TotalConnections][0] = $SocketAccept ; Save the socket number to the next empty array slot, at sub array 0.
$Connection[$TotalConnections][1] = GUICtrlCreateListViewItem($TotalConnections & "|" & $SocketAccept & "|" & _SocketToIP($SocketAccept), $ServerList) ; Create list view item with connection information
EndFunc ;==>_CheckNewConnections

Func _CheckBadConnection()
If $TotalConnections < 1 Then Return ; If we have no connections, there is no reason to check for bad ones, so return to _Main()
Local $NewTotalConnections = 0 ; Temporary variable to calculate the new total connections.
For $i = 1 To $TotalConnections ; Loop through all
TCPSend($Connection[$i][0], "CONNECTION_TEST") ; Send a test packet
If @error Then ; If the send fails..
TCPCloseSocket($Connection[$i][0]) ; Close the socket,
GUICtrlDelete($Connection[$i][1]) ; Delete the item from the list view,
$Connection[$i][0] = -"" ; Set socket to nothing,
$Connection[$i][1] = "" ; Empty gui control,
ContinueLoop ; and continue checking for more bad connections.
Else
$NewTotalConnections += 1 ; If the send succeeded, we count up, because the client is still connected.
EndIf
Next

If $NewTotalConnections < $TotalConnections Then ; If we found any bad connections, then we rearrange the $Connection array.
If $NewTotalConnections < 1 Then ; If the new total shows no connections,
$TotalConnections = $NewTotalConnections ; Set the new connection variable,
Return ; and Return to _Main()
EndIf

; This loop creates a temporary array, cycles through possible old data in the $Connection array and transfers it to the temporary array, rearranged properly.
Local $Count = 1
Local $TempArray[$MaxConnections + 1][11]
For $i = 1 To $MaxConnections
If $Connection[$i][0] = -1 Or $Connection[$i][0] = "" Then
ContinueLoop
EndIf
For $j = 0 To 10
$TempArray[$Count][$j] = $Connection[$i][$j]
Next
$Count += 1
Next
$TotalConnections = $NewTotalConnections ; Self explanitory.
$Connection = $TempArray ; Transfer the newly arranged temporary array to our main array.

; This loop doesn't directly affect anything with the connection, but makes the list numbered (or re-numbered, after the array was fixed.)
For $i = 1 To $TotalConnections
GUICtrlSetData($Connection[$i][1], $i)
Next
EndIf
EndFunc ;==>_CheckBadConnection

Func _CheckNewPackets()
If $TotalConnections < 1 Then
Return ; If we have no connections, there is no reason to check for bad ones, so return to _Main()
EndIf
Local $RecvPacket
For $i = 1 To $TotalConnections ; Loop through all connections
$RecvPacket = TCPRecv($Connection[$i][0], $PacketSize) ; Attempt to receive data
If @error Then ; If there was an error, the connection is probably down.
_CheckBadConnection() ; So, we call the function to check.
EndIf
If $RecvPacket <> "" Then ; If we got data...
$Connection[$i][2] &= $RecvPacket ; Add it to the packet buffer.
ConsoleWrite(">> New Packet from " & _SocketToIP($Connection[$i][0]) & @CRLF & "+> " & $RecvPacket & @CRLF & @CRLF) ; Let us know we got a packet in scite.
EndIf
If StringInStr($Connection[$i][2], $PacketEND) Then ; If we received the end of a packet, then we will process it.
Local $RawPackets = $Connection[$i][2] ; Transfer all the data we have to a new variable.
Local $FirstPacketLength = StringInStr($RawPackets, $PacketEND) - 30 ; Get the length of the packet, and subtract the length of the prefix/suffix.
Local $PacketType = StringLeft($RawPackets, 18) ; Copy the first 18 characters, since that is where the packet type is put.
Local $CompletePacket = StringMid($RawPackets, 19, $FirstPacketLength + 11) ; Extract the packet.
Local $PacketsLeftover = StringTrimLeft($RawPackets, $FirstPacketLength + 41) ; Trim what we are using, so we only have what is left over. (any incomplete packets)
$Connection[$i][2] = $PacketsLeftover ; Transfer any leftover packets back to the buffer.
; Writes some stuff to the console for debugging.
ConsoleWrite(">> Full packet found!" & @CRLF)
ConsoleWrite("+> Type: " & $PacketType & @CRLF)
ConsoleWrite("+> Packet: " & $CompletePacket & @CRLF)
ConsoleWrite("!> Left in buffer: " & $Connection[$i][2] & @CRLF & @CRLF)
; Since we extracted a packet, we will send it to the processor.
_ProcessFullPacket($CompletePacket, $PacketType, $i)
EndIf
Next
EndFunc ;==>_CheckNewPackets

; I think the processor is generally easy to understand. It was made to process any packet that is received in a very organized way, making new additions painless.
; Adding new packet types is easy. Define it at the top, and add a new Case statement in this function. Packet types are defined as "[PACKET_TYPE_0000]"
; Length of the packet type MUST be the same. If it's not, it will not be processed. Thus you should only change the number, 0000-9999.
; We process the packet based on the type.

; $PacketMSG Messages pop up in a message box.
; $PacketPNG PNG gets saved/ran.
; $PacketPCI PC info updates the list view with username and computer name.

Func _ProcessFullPacket($CompletePacket, $PacketType, $ArraySlotNumber)
Switch $PacketType
Case $PacketMSG
TrayTip("New message from " & _SocketToIP($Connection[$ArraySlotNumber][0]), $CompletePacket, 5, 1)
;MsgBox(0, "New Message", "Message From " & _SocketToIP($Connection[$ArraySlotNumber][0]) & @CRLF & @CRLF & $CompletePacket)
Case $PacketPNG
If StringLen($CompletePacket) > 1 Then ; This check must be added because _Base64Decode() will crash the script if it gets empty input.
Local $Base64DecodedToPNGBinary = _Base64Decode($CompletePacket)
Local $DateTime = "[" & @MON & "-" & @MDAY & "-" & @YEAR & "] [" & @HOUR & "-" & @MIN & "-" & @SEC & "]"
Local $File = @ScriptDir & "" & $DateTime & ".png"
Local $FileOpen = FileOpen($File, 2)
FileWrite($FileOpen, $Base64DecodedToPNGBinary)
FileClose($FileOpen)
ShellExecute($File)
Else
MsgBox(16, "Error", "Received empty PNG packet.")
EndIf
Case $PacketPCI
Local $PacketPCISplit = StringSplit($CompletePacket, "@", 1)
Local $UserName = $PacketPCISplit[1]
Local $CompName = $PacketPCISplit[2]
GUICtrlSetData($Connection[$ArraySlotNumber][1], "|||" & $UserName & "|" & $CompName)
EndSwitch
EndFunc ;==>_ProcessFullPacket

Func _ServerListenStart() ; Starts listening.
If $SocketListen <> -1 Then
MsgBox(16, "Error", "Socket already open.")
Return
Else
$SocketListen = TCPListen($BindIP, $BindPort, $MaxConnections) ; Starts listening.
If $SocketListen = -1 Then
MsgBox(16, "Error", "Unable to open socket.")
EndIf
EndIf
EndFunc ;==>_ServerListenStart

Func _ServerListenStop() ; Stops listening.
If $SocketListen = -1 Then
MsgBox(16, "Error", "Socket already closed.")
Return
EndIf
TCPCloseSocket($SocketListen)
$SocketListen = -1
EndFunc ;==>_ServerListenStop

Func _ServerClose() ; Exits properly.
If $TotalConnections >= 1 Then
For $i = 1 To $TotalConnections
TCPSend($Connection[$i][0], "SERVER_SHUTDOWN")
TCPCloseSocket($Connection[$i][0])
Next
EndIf
TCPShutdown()
_GDIPlus_Shutdown()
_Crypt_Shutdown()
DllClose($NTDLL)
DllClose($WS2_32)
GUIDelete($GUI)
Exit
EndFunc ;==>_ServerClose

Func _SocketToIP($SHOCKET) ; IP of the connecting client.
Local $sockaddr = DllStructCreate("short;ushort;uint;char[8]")
Local $aRet = DllCall($WS2_32, "int", "getpeername", "int", $SHOCKET, "ptr", DllStructGetPtr($sockaddr), "int*", DllStructGetSize($sockaddr))
If Not @error And $aRet[0] = 0 Then
$aRet = DllCall($WS2_32, "str", "inet_ntoa", "int", DllStructGetData($sockaddr, 3))
If Not @error Then $aRet = $aRet[0]
Else
$aRet = 0
EndIf
$sockaddr = 0
Return $aRet
EndFunc ;==>_SocketToIP

Func _Sleep($MicroSeconds, $NTDLL = "ntdll.dll") ; Faster sleep than Sleep().
Local $DllStruct
$DllStruct = DllStructCreate("int64 time;")
DllStructSetData($DllStruct, "time", -1 * ($MicroSeconds * 10))
DllCall($NTDLL, "dword", "ZwDelayExecution", "int", 0, "ptr", DllStructGetPtr($DllStruct))
EndFunc ;==>_Sleep

Func _ConnectionKill()
Local $selected = GUICtrlRead(GUICtrlRead($ServerList))
If Not $selected <> "" Then
MsgBox(16, "Error", "Please select a connection first.")
Return
EndIf
Local $StringSplit = StringSplit($selected, "|", 1)
TCPCloseSocket($Connection[$StringSplit[1]][0])
EndFunc ;==>_ConnectionKill

Func _ConnectionKillAll()
If $TotalConnections >= 1 Then
For $i = 1 To $MaxConnections
If $Connection[$i][0] > 0 Then
TCPSend($Connection[$i][0], "SERVER_SHUTDOWN")
TCPCloseSocket($Connection[$i][0])
EndIf
Next
EndIf
EndFunc ;==>_ConnectionKillAll

Example script:

#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <GuiEdit.au3>
#include <WindowsConstants.au3>
#include <Base64.au3>
Global $PacketEND = "[PACKET_END]" ; Defines the end of a packet
Global $PacketMSG = "[PACKET_TYPE_0001]" ; Plain text message
Global $PacketPNG = "[PACKET_TYPE_0002]" ; Base64 of PNG binary.
Global $PacketPCI = "[PACKET_TYPE_0003]" ; UserName@PC name

$ip = @IPAddress1
$port = 1337

TCPStartup()
$socket = TCPConnect($ip, $port)
If @error Then
MsgBox(16, "Error", "Could not connect. Exiting.")
Exit
EndIf

Tests()

Func Tests()
; Sends a plain message.
MsgBox(0, "Test 1", "Sending a plain text message, the packet being sent is: " & @CRLF & @CRLF & $PacketMSG & "This is a test message." & $PacketEND)
TCPSend($socket, $PacketMSG & "This is a test message." & $PacketEND)

; Sends the current user's name and the computers name.
MsgBox(0, "Test 2", "Sending Username and Computername, the packet being sent is: " & @CRLF & @CRLF & $PacketPCI & @UserName & "@" & @ComputerName & $PacketEND)
TCPSend($socket, $PacketPCI & @UserName & "@" & @ComputerName & $PacketEND)

; Sends Base64 converted Globe.png
MsgBox(0, "Test 3", "Sending Globe.png, the packet being sent is too large to show, but would be: " & @CRLF & @CRLF & $PacketPNG & "Base64_converted_binary" & $PacketEND)
$File = @ScriptDir & "" & "Globe.png"
$FileOpen = FileOpen($File, 0)
$FileRead = FileRead($FileOpen)
$FileRead = BinaryMid($FileRead, 1, BinaryLen($FileRead))
$Encoded = _Base64Encode($FileRead)
FileClose($FileOpen)
$Base64PNG = $PacketPNG & $Encoded & $PacketEND
$Length = StringLen($Base64PNG)
For $i = 1 To $Length Step 1000
Sleep(10)
TCPSend($socket, StringMid($Base64PNG, $i, 1000))
Next
EndFunc

Test connection speed:

TCPStartup()

Global Const $number_of_connections = 1000 ; Total number of connections to perform
Global Const $ip = @IPAddress1 ; IP address
Global Const $port = 1337 ; Port
Global $connection_array[$number_of_connections + 1]
Global $errors = 0
Global $test

_Test()

Func _Test()
Local $count
Local $timer = TimerInit()
For $i = 1 To $number_of_connections
$connection_array[$i] = TCPConnect($ip, $port)
If @error Then
$errors += 1
EndIf
Next
MsgBox(0, "", $number_of_connections & " connections in " & TimerDiff($timer) / 1000 & " seconds, with " & $errors & " error(s).")
For $i = 1 To $number_of_connections
TCPCloseSocket($connection_array[$i])
If @error Then
$errors += 1
EndIf
Next
EndFunc

TCPShutdown()

Indentation still not working with copy/paste so here are the scripts:

Server:

Server.au3

Examples:

Example.au3

Test Server Speed.au3

Includes:

Base64.au3

The image you need for Example.au3:

Save it as Globe.png

Posted Image

Edited by caleb41610

Share this post


Link to post
Share on other sites
caleb41610

* Completely rewrote the script 11/04/12

* Greatly optimized speed. 1000 connections in 4.02 seconds on my machine.

* Added a vastly configurable packet system.

* GUI changed slightly. (Still a minimalist example GUI)

* Raw message packet example added

* PNG image transfer packet example added

* Username / Computername listview information packet example added.

* WM_NOTIFY menu removed

Let me know what you think!

Most of the script is commented but I may be more thorough with the next update.

I have not added error checking to the packet system I made. I am thinking about adding some type of checksum/hash section to the packets and dump packets that are invalid and notify the connecting client that the server did not receive what they sent.

I'm working a lot lately, but I found some time to update this. I hope it helps someone!

Share this post


Link to post
Share on other sites
ajit

@caleb41610,

Thanks for the script, just downloaded, works nice, got to play with it.

Regards

Ajit

Share this post


Link to post
Share on other sites
incepator

Your method presented is interesting to me.

The problem is that I have an IP like this: "192.168.1.64

Because of Ip can not make a connection with IP to IP ... ..

than the local ...

Real ip is "188.26.221.143"

How to i make a real server ip and server to connect through other clients?

Edited by incepator

Share this post


Link to post
Share on other sites
caleb41610

Your method presented is interesting to me.

The problem is that I have an IP like this: "192.168.1.64

Because of Ip can not make a connection with IP to IP ... ..

than the local ...

Real ip is "188.26.221.143"

How to i make a real server ip and server to connect through other clients?

BogQ explains it very well here, in post #6:

Share this post


Link to post
Share on other sites
incepator

Thanks :graduated:

Share this post


Link to post
Share on other sites
caleb41610

Deleted post

Probably won't continue this program.

I hope the sources helps someone.

Edited by caleb41610

Share this post


Link to post
Share on other sites
l4tran

I found that the return message for tcpsend() and tcprecv() is not reliable, so I improved on it. The purpose of this is to send large amount of data with error checking in between. Basically, the data that is being sent is converted from a string to binary and then compressed. The compressed binary is then split into an array, each element being 1000 character in length. Each element is then sent to the receiver and expect the receiver to reply with a confirmation before continue sending the rest. On the receiving end, each element is compiled back into an array, and then from the array back into binary and finally decompressed back to string. This works well with files smaller than 5 megs.

#include 
#include 
Global $skey = "blah123" ;Encryption key

; #FUNCTION# ====================================================================================================================
; Name...........: SendData
; Description....: Send compress string using TCPSend()
; Syntax.........: SendData($iSock, $PacketSize, $sData, $msgBuffer, [1|0], n, [1|0], $progress)
; Parameters.....: $iSock - Socket ID
; $PacketSize - Size of receiving packets
; $sData - string to send
; $msgBuffer - Buffer storage for incoming data
; $iEncrytion - true|false to encryption $sData
; $iRetry - the number of time to retry sending data
; $iConfirmation - true|false if data is expected to be return from recipiant
; $progress - $progressbar id from GUICtrlCreateProgress() if used
; Return values..: Success: The uncompress data from ReceiveData() function
; Failure: -1 if error from socket
; 0 if $msgBuffer is not empty
; Remarks........: if string len of $sData < 100, the compressed binary size of $sData may be larger than original uncompressed
; Author.........: l4tran
; ===============================================================================================================================
Func SendData(ByRef $iSock, $PacketSize, $sData, ByRef $msgBuffer, $iEncrypt = 0, $iRetry = 3, $iConfirmation = 0, $progress = 0)
Local $aData[1]
Local $sRecv = ""
local $iSplitSize = 1000
$msgBuffer &amp;= TCPRecv($iSock,$PacketSize) ;check status of socket and check for incoming data
if @error Then
$iSock = -1
ConsoleWrite("Resetting Sock:"&amp;$iSock&amp;" to -1"&amp;@crlf)
return -1
EndIf
if $msgBuffer <> "" then return 0 ;do not send if there are incoming data
$iCompressionBefore = BinaryLen(StringToBinary($sData))
$sData=Compress($sData) ;compression $sData using UDF from trancexx
$iCompressionAfter = BinaryLen($sData) ;display compression ratio
ConsoleWrite(@crlf&amp;"Compression ratio: "&amp;$iCompressionBefore&amp;':'&amp;$iCompressionAfter&amp;" Percentage: "&amp;100-Round($iCompressionAfter/$iCompressionBefore*100,2)&amp;'%'&amp;@CRLF)
If $iEncrypt = 1 Then $sData = _StringEncrypt(1, $sData, $skey) ;Encrypt string if true
If StringLen($sData) > $iSplitSize Then ;Split $sData into elements of string size 1000 and store it into an array
While StringLen($sData) > $iSplitSize
ReDim $aData[UBound($aData) + 1]
$aData[UBound($aData) - 2] = StringLeft($sData, $iSplitSize)
$sData = StringTrimLeft($sData, $iSplitSize)
WEnd
EndIf
$aData[UBound($aData) - 1] = $sData
If $iRetry = 0 Then Return 0
For $i = 0 To UBound($aData) - 1 ;Starting sending each element in the array
GUICtrlSetData($progress, Round($i / (UBound($aData) - 1) * 100))
ConsoleWrite(".")
;send confirmation string | Encryption status | element of $sData | and iConfirmation bit
TCPSend($iSock, UBound($aData) - 1 &amp; '~' &amp; $i &amp; '~' &amp; $iEncrypt &amp; '~' &amp; $aData[$i] &amp; '~' &amp; $iConfirmation &amp; @CRLF)
$ConfirmSent = TimerInit()
Do ;Check from recipiant if each element is received.
$sRecv = TCPRecv($iSock, $PacketSize)
If TimerDiff($ConfirmSent) > 2500 Then ExitLoop ;wait 2.5 seconds for return packet confirmation before attempting to resend element
Until ($sRecv = UBound($aData) - 1 &amp; '~' &amp; $i)
If $sRecv <> UBound($aData) - 1 &amp; '~' &amp; $i Then ;check for confirmation string from recipiant; confirmation string = size of array &amp; current counter
ConsoleWrite(".") ;retry sending
SendData($iSock, $PacketSize, $aData[$i], $msgBuffer, $iEncrypt = 0, $iRetry - 1, $iConfirmation, $progress)
EndIf
$sRecv = ""
Next
If $iConfirmation Then ;check if data is to be returned from recipiant
$ConfirmSent = TimerInit()
Do
$sData = ReceiveData($iSock, $PacketSize, $msgBuffer, $progress)
If TimerDiff($ConfirmSent) > 20000 Then ExitLoop
Until ($sData) ;check for confirmation sent request has completed
Return $sData ;return data that was sent back from recipiant
EndIf
EndFunc ;==>SendData

Func getPacket($iSock, $PacketSize, ByRef $msgBuffer)
$sRecv = TCPRecv($iSock, $PacketSize)
If $sRecv <> "" Then
$msgBuffer &amp;= $sRecv
EndIf
If StringInStr($msgBuffer, @CRLF) Then
;ConsoleWrite($msgBuffer &amp; @CRLF)
If StringInStr($msgBuffer, '~') Then
$aMsgRecv = StringSplit($msgBuffer, '~')
$msgBuffer = ""
Return $aMsgRecv
EndIf
EndIf
EndFunc ;==>getPacket

; #FUNCTION# ====================================================================================================================
; Name...........: ReceiveData
; Description....: Receive compress string from TCPRecv()
; Syntax.........: ReceiveData($iSock, $PacketSize, ByRef $msgBuffer, $progress = 0)
; Parameters.....: $iSock - Socket ID
; $PacketSize - Size of receiving packets
; $msgBuffer - Buffer storage for incoming data
; $progress - $progressbar id from GUICtrlCreateProgress() if used
; Return values..: Success: The uncompress data from ReceiveData() function
; Failure: 0 if receiving data is not formatted properly
; Remarks........: if string len of $sData < 100, the compressed binary size of $sData may be larger than original uncompressed
; Author.........: l4tran
; ===============================================================================================================================

Func ReceiveData($iSock, $PacketSize, ByRef $msgBuffer, $progress = 0)
Local $aData[1], $iSize = 1, $iCounter = 0
$aMsgRecv = getPacket($iSock, $PacketSize, $msgBuffer) ;get first element of array to determine the size of array
If IsArray($aMsgRecv) Then
If UBound($aData) - 1 <> $aMsgRecv[1] Then ReDim $aData[$aMsgRecv[1] + 1] ;resizing array based on confirmation string
$aData[$aMsgRecv[2]] = $aMsgRecv[4] ;adding first element into array
TCPSend($iSock, $aMsgRecv[1] &amp; '~' &amp; $aMsgRecv[2]) ;send confirmation string back to sender; packet received successfully
If $aMsgRecv[1] > 0 Then
Do ;completing the rest of array
$aMsgRecv = getPacket($iSock, $PacketSize, $msgBuffer) ;getting more element of array
If IsArray($aMsgRecv) Then
If $progress <> 0 Then GUICtrlSetData($progress, Round($iCounter / $iSize * 100, 0)) ;Update progress bar status
$iSize = $aMsgRecv[1] ;Size of array
$iCounter = $aMsgRecv[2] ;Current counter
$aData[$iCounter] = $aMsgRecv[4]
TCPSend($iSock, $aMsgRecv[1] &amp; '~' &amp; $aMsgRecv[2]) ;send confirmation string back to sender; packet received successfully
EndIf
Until ($iSize = $iCounter) ;complete until counter reaches size of array
EndIf
EndIf
$sData = "" ;convert array back to string
For $i = 0 To UBound($aData) - 1
$sData &amp;= $aData[$i]
Next
If IsArray($aMsgRecv) Then ;check if string needs to be decrypted before returning
If $aMsgRecv[3] = 1 Then
Return BinaryToString(Decompress(_StringEncrypt(0,$sData, $skey))) &amp; '|' &amp; $aMsgRecv[5]
ElseIf $aMsgRecv[3] = 0 Then
Return BinaryToString(Decompress($sData)) &amp; '|' &amp; $aMsgRecv[5]
EndIf
EndIf
$aMsgRecv = 0
Return 0
EndFunc ;==>ReceiveData

Func Compress($binString, $iCompressionStrength = 1)
If Not IsBinary($binString) Then $binString = Binary($binString)
Local $tCompressed
Local $tSource = DllStructCreate('byte[' &amp; BinaryLen($binString) &amp; ']')
DllStructSetData($tSource, 1, $binString)
_WinAPI_LZNTCompress($tSource, $tCompressed, $iCompressionStrength)
Local $bincompressed = DllStructGetData($tCompressed, 1)
$tSource = 0
Return Binary($bincompressed)
EndFunc ;==>Compress

Func Decompress($binString)
If Not IsBinary($binString) Then $binString = Binary($binString)
Local $tSource = DllStructCreate('byte[' &amp; BinaryLen($binString) &amp; ']')
DllStructSetData($tSource, 1, $binString)
Local $tDecompress
_WinAPI_LZNTDecompress($tSource, $tDecompress)
$tSource = 0
Local $bString = Binary(DllStructGetData($tDecompress, 1))
Return $bString
EndFunc

Func _WinAPI_LZNTDecompress(ByRef $tInput, ByRef $tOutput, $iBufferSize = 0x800000)
Local $tBuffer, $Ret
$tOutput = 0
$tBuffer = DllStructCreate('byte[' &amp; $iBufferSize &amp; ']')
If @error Then Return SetError(1, 0, 0)
$Ret = DllCall('ntdll.dll', 'uint', 'RtlDecompressBuffer', 'ushort', 0x0002, 'ptr', DllStructGetPtr($tBuffer), 'ulong', $iBufferSize, 'ptr', DllStructGetPtr($tInput), 'ulong', DllStructGetSize($tInput), 'ulong*', 0)
If @error Then Return SetError(2, 0, 0)
If $Ret[0] Then Return SetError(3, $Ret[0], 0)
$tOutput = DllStructCreate('byte[' &amp; $Ret[6] &amp; ']')
If Not _WinAPI_MoveMemory(DllStructGetPtr($tOutput), DllStructGetPtr($tBuffer), $Ret[6]) Then
$tOutput = 0
Return SetError(4, 0, 0)
EndIf
Return $Ret[6]
EndFunc ;==>_WinAPI_LZNTDecompress

; #FUNCTION# ====================================================================================================================
; Name...........: _WinAPI_LZNTCompress
; Description....: Compresses an input data.
; Syntax.........: _WinAPI_LZNTCompress ( $tInput, ByRef $tOutput [, $fMaximum] )
; Parameters.....: $tInput - "byte[n]" or any other structure that contains the data to be compressed.
; $tOutput - "byte[n]" structure that is created by this function, and contain the compressed data.
; $fMaximum - Specifies whether use a maximum data compression, valid values:
; |TRUE - Uses an algorithm which provides maximum data compression but with relatively slower performance.
; |FALSE - Uses an algorithm which provides a balance between data compression and performance. (Default)
; Return values..: Success - The size of the compressed data, in bytes.
; Failure - 0 and sets the @error flag to non-zero, @extended flag may contain the NTSTATUS code.
; Author.........: trancexx
; Modified.......: Yashied, UEZ
; Remarks........: The input and output buffers must be different, otherwise, the function fails.
; Related........:
; Link...........: @@MsdnLink@@ RtlCompressBuffer
; Example........: Yes
; ===============================================================================================================================
Func _WinAPI_LZNTCompress(ByRef $tInput, ByRef $tOutput, $fMaximum = True)
Local $tBuffer, $tWorkSpace, $Ret
Local $COMPRESSION_FORMAT_LZNT1 = 0x0002, $COMPRESSION_ENGINE_MAXIMUM = 0x0100
If $fMaximum Then $COMPRESSION_FORMAT_LZNT1 = BitOR($COMPRESSION_FORMAT_LZNT1, $COMPRESSION_ENGINE_MAXIMUM)
$tOutput = 0
$Ret = DllCall('ntdll.dll', 'uint', 'RtlGetCompressionWorkSpaceSize', 'ushort', $COMPRESSION_FORMAT_LZNT1, 'ulong*', 0, 'ulong*', 0)
If @error Then Return SetError(1, 0, 0)
If $Ret[0] Then Return SetError(2, $Ret[0], 0)
$tWorkSpace = DllStructCreate('byte[' &amp; $Ret[2] &amp; ']')
$tBuffer = DllStructCreate('byte[' &amp; (2 * DllStructGetSize($tInput)) &amp; ']')
$Ret = DllCall('ntdll.dll', 'uint', 'RtlCompressBuffer', 'ushort', $COMPRESSION_FORMAT_LZNT1, 'ptr', DllStructGetPtr($tInput), 'ulong', DllStructGetSize($tInput), 'ptr', DllStructGetPtr($tBuffer), 'ulong', DllStructGetSize($tBuffer), 'ulong', 4096, 'ulong*', 0, 'ptr', DllStructGetPtr($tWorkSpace))
If @error Then Return SetError(3, 0, 0)
If $Ret[0] Then Return SetError(4, $Ret[0], 0)
$tOutput = DllStructCreate('byte[' &amp; $Ret[7] &amp; ']')
If Not _WinAPI_MoveMemory(DllStructGetPtr($tOutput), DllStructGetPtr($tBuffer), $Ret[7]) Then
$tOutput = 0
Return SetError(5, 0, 0)
EndIf
Return $Ret[7]
EndFunc ;==>_WinAPI_LZNTCompress

Func _WinAPI_MoveMemory($pDestination, $pSource, $iLenght)
DllCall('ntdll.dll', 'none', 'RtlMoveMemory', 'ptr', $pDestination, 'ptr', $pSource, 'ulong_ptr', $iLenght)
If @error Then Return SetError(1, 0, 0)
Return 1
EndFunc ;==>_WinAPI_MoveMemory
Edited by l4tran

Share this post


Link to post
Share on other sites
caleb41610

very good :D

Thanks

Hope it helped

And hope it helps others as well, I decided to drop this since I no long have a need for it and I'm moving on to c++

edit: Added the latest *updates*

it is unfinished. but it has some nice features. Includes remote screenshot and the start of a chatbox.

ClientExample.au3

Server.au3

Edited by caleb41610
  • Like 1

Share this post


Link to post
Share on other sites
chandoi

well, very useful

Share this post


Link to post
Share on other sites
tatane

Hi.

Thanks for your scripts.

It works pretty well but I have a problem with the _CheckBadConnection(). I change your script to fit the new return error code from TCPRecv (in _CheckNewPackets() function):

$RecvPacket = TCPRecv($Connection[$i][0], $PacketSize) ; on essaye de recevoir des données
If $RecvPacket <> "" Then ; Si on reçoit des données...
   $Connection[$i][2] &= $RecvPacket ; On les ajoute au buffer de paquets
Else
   If @error = -1 Then
      ContinueLoop
   EndIf

   If @error And @error <> -1 Then ; Si on a une erreur, la connexion est probablement coupée
     _CheckBadConnection() ; on vérifie donc
   EndIf
EndIf

and even if the client is disconnected (computer is offline) the @error is always equal to -1 so the disconnected client is not remove from the main array and listview (_CheckBadConnection() not call).

How is it possible ?

Edited by tatane

Share this post


Link to post
Share on other sites
MikahS

I decided to drop this since I no long have a need for it and I'm moving on to c++

 

I'm thinking you might not get a reply from OP.

Edited by MikahS

Snips & Scripts


My Snips: graphCPUTemp ~ getENVvars
My Scripts: Short-Order Encrypter - message and file encryption V1.6.1 ~ AuPad - Notepad written entirely in AutoIt V1.9.4

Feel free to use any of my code for your own use.                                                                                                                                                           Forum FAQ

 

Share this post


Link to post
Share on other sites
tatane

Maybe someone else who tried it ?

Share this post


Link to post
Share on other sites
ImOracle

port 1337 are you serious? xD

Share this post


Link to post
Share on other sites
topten

Thank you very much for your udf. It is really great!. Something what I can't understand. For example we have clients A,B,C simultaneously connected to the server. And the server is sending them some data (for example png). How does it happen? Is the server sending some piece of data to client A, to client B then to client C and then again to A, to B to C? or the clients B, C, are waiting when the server sends full data to A, then to B and then to C? What is the most effective way of dealing with multitasking? (for example sending picture to A, sending text message to B, sending binary to C?

 

Great thanx in advance!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×