Jump to content

TCP Internal Server *Updates*


themax90
 Share

Recommended Posts

Fixed: $MaxConnection and $MaxConc unneed of double variable

Fixed: Easier StringAnalyze Method (Clarity for ITS Servers to Come)

Fixed: All Variables Declared

Fixed: TCPSendMessageAll Failure = OpenSocket

Added: Maximum Connection Protection

Added: PriorityQueue by nfwu (ITS Devel Team)

Added: /hello Function for fun :whistle:

Added: Socket Recognition(For easier to read streams)

Added: Customizable Server Stream Prefix on $Prefix Variable

Change: "~" function prefix is now Chr(0)

Hopefully it is more user friendly and a bit quicker to use. Esp. since the messages are now handled in the order they are received.

TCP Internal Server *Update* : See My Fileman

http://www.autoitscript.com/fileman/users/AutoIt%20Smith/

If you want to copy and paste:

Global $PRIORITYQUEUE_ITEM = ""
Global Enum $DELAY_AFTERPREV = 0, $DELAY_FROMNOW = 1
Global Const $PRIORITYQUEUE_DEFAULTLIMIT = 100
Global Const $PRIORITYQUEUE_FIRSTITEM = 2
Global Const $PRIORITYQUEUE_NEGATIVEPOINTER = $PRIORITYQUEUE_FIRSTITEM - 1
; Only edit the 4 commented variables.
Global Const $Port = 8000; Set your port here.
Global Const $MaxLength = 512; Maximum message length.
Global Const $MaxConnection = 100; Maximum user connections
Global Const $Prefix = Chr(0); Server stream prefix.  (EDIT IN CLIENT AS WELL)
Global $Queue = _PriorityQueueCreate()
Global $MainSocket = TCPStartServer($Port, ($MaxConnection + 1))
If @error <> 0 Then Exit MsgBox(16, "Error", "Server unable to initialize.")
Global $ConnectedSocket[ ($MaxConnection + 1) ]
Global $CurrentSocket = 0
Local $Track = 0
Local $Data
For $Track = 0 To $MaxConnection Step 1
    $ConnectedSocket[$Track] = -1
Next
While 1
    $ConnectedSocket[$CurrentSocket] = TCPAccept($MainSocket)
    Select
        Case $ConnectedSocket[$CurrentSocket] <> - 1
            $CurrentSocket = SocketSearch()
            Select
                Case $CurrentSocket = $MaxConnection
                    TCPSend($ConnectedSocket[$CurrentSocket], "Server Full")
                    OpenSocket($CurrentSocket)
            EndSelect
    EndSelect
    $Track = 0
    For $Track = 0 To $MaxConnection Step 1
        If $ConnectedSocket[$Track] <> - 1 Then
            $Data = TCPRecv($ConnectedSocket[$Track], $MaxLength)
            Select
                Case $Data = $Prefix & "bye"
                    OpenSocket($Track)
                Case $Data <> ""
                    _PriorityQueueAdd($Queue, $Data & Chr(0) & $Track, 0020, $DELAY_AFTERPREV)
            EndSelect
        EndIf
    Next
    _PriorityQueueCheck($Queue, "StringAnalyze")
WEnd
Func OpenSocket($SocketNumber)
    TCPSend($ConnectedSocket[$SocketNumber], $Prefix & "bye")
    TCPCloseSocket($ConnectedSocket[$Track])
    $ConnectedSocket[$Track] = -1
    $CurrentSocket = SocketSearch()
EndFunc  ;==>OpenSocket
Func StringAnalyze($String)
    $String = StringSplit($String, Chr(0))
    Local $Data = $String[1]
    Local $SocketNumber = $String[2]
    Select
        Case $Data = "/hello"
            TCPSend($ConnectedSocket[$SocketNumber], "Server>>> Hello there from the server.")
        Case Else
            TCPSendMessageAll($MaxConnection, "Socket " & $SocketNumber & ">>>" & $Data)
    EndSelect
EndFunc  ;==>StringAnalyze
Func TCPSendMessageAll($ConnectionLimit, $Data)
    Local $Track = 0
    For $Track = 0 To $ConnectionLimit Step 1
        If $ConnectedSocket[$Track] <> - 1 Then 
            If TCPSend($ConnectedSocket[$Track], $Data) = 0 Then OpenSocket($Track)
        EndIf
    Next
EndFunc  ;==>TCPSendMessageAll
Func TCPStartServer($Port, $MaxConnect = 1)
    Local $Socket
    $Socket = TCPStartup()
    Select
        Case $Socket = 0
            SetError(@error)
            Return -1
    EndSelect
    $Socket = TCPListen(@IPAddress1, $Port, $MaxConnect)
    Select
        Case $Socket = -1
            SetError(@error)
            Return 0
    EndSelect
    SetError(0)
    Return $Socket
EndFunc  ;==>TCPStartServer
Func SocketSearch()
    Local $Track = 0
    For $Track = 0 To $MaxConnection Step 1
        If $ConnectedSocket[$Track] = -1 Then Return $Track
    Next
EndFunc  ;==>SocketSearch
Func _PriorityQueueCreate($Limit = 100)
    Local $q[$Limit + $PRIORITYQUEUE_FIRSTITEM][2]
    $q[0][0] = $Limit   ;;Size
    $q[0][1] = TimerInit()  ;;Timer
    $q[1][0] = $PRIORITYQUEUE_FIRSTITEM;;Read Pointer
    $q[1][1] = $PRIORITYQUEUE_FIRSTITEM;;Write Pointer
    $q[$Limit][1] = $q[0][1]
    Return $q
EndFunc  ;==>_PriorityQueueCreate
Func _PriorityQueueAdd(ByRef $q, $item, $delay = 0020, $type = 0);; $DELAY_AFTERPREV)
    $q[$q[1][1]][0] = $item
    Switch $type
        Case $DELAY_AFTERPREV
            $q[$q[1][1]][1] = $q[_PriorityQueuePointerWithinBounds($q, $q[1][1] - 1) ][1] + $delay
        Case $DELAY_FROMNOW
            $q[$q[1][1]][1] = TimerDiff($q[0][1]) + $delay
    EndSwitch
    $q[1][1] = _PriorityQueuePointerWithinBounds($q, $q[1][1] + 1)
EndFunc  ;==>_PriorityQueueAdd
Func _PriorityQueueCheck(ByRef $q, $nameoffunctiontocall, $only1percall = 0)
    Local $currtime = TimerDiff($q[0][1])
    While $q[$q[1][0]][1] <> 0
    ;;MsgBox(0, "", "B4");;DEBUG
        If $q[$q[1][0]][1] > $currtime Then ExitLoop
    ;;MsgBox(0, "", "OK");;DEBUG
        Call($nameoffunctiontocall, $q[$q[1][0]][0])
        $q[$q[1][0]][1] = 0
        $q[1][0] = _PriorityQueuePointerWithinBounds($q, $q[1][0] + 1)
        If $only1percall Then ExitLoop
    WEnd
EndFunc  ;==>_PriorityQueueCheck
Func _PriorityQueuePointerWithinBounds($q, $ptr)
    While $ptr >= $q[0][0] + $PRIORITYQUEUE_FIRSTITEM
        $ptr -= $q[0][0]
    WEnd
    While $ptr <= $PRIORITYQUEUE_NEGATIVEPOINTER
        $ptr += $q[0][0]
    WEnd
    Return $ptr
EndFunc  ;==>_PriorityQueuePointerWithinBounds

Please also check my post on TCP in my signature. For a basic client, please use TCP Communicator and edit the part where it says the following on line 22:

$TCPSent = TCPSend($MainSocket, $Text & @LF)

Remove the & @LF and it should be set to work with this server.

Thank you for supporting the AutoIt and AutoIt-ITS Community.

AutoIt Smith

ITS Devel Team

Edited by AutoIt Smith
Link to comment
Share on other sites

Tutorial on Writing Functions:

The section to edit is called StringAnalyze. The information you will be supplied with is the sent string in the variable $Data and the socket number from which it was sent in $SocketNumber. As you see in the function /hello, if the $Data (sent text) = /hello (the command) then the server sends to the ConnectedSocket[$SocketNumber] a message of Hello back. This can be used with similar functions.

First thing to do is to ask this question, "Is the command in the string?". The StringInStr allows me to do this. We do not use $Data = "/name" because this has definable syntax where "/hello" does not. There are two different types of commands. Non-defined and Defined.

A Non-defined command is a simple command shown by a prefix (in this case "/") followed by the command name ("hello") with no syntax. If it is sent with syntax then it is not that exact function. This allows you to have a "quick" function and a "defined" function of the same name. (Perhaps like "/ping"). When writing functions to determine between a Non-Defined and Defined function, you should notice that Non-Defined functions are $Data = %function%, while Defined functions are StringInStr.

Defined functions use StringInStr because the exact function is not delimited to the function prefix and name. If it is found in the whole string it then StringSplit's the whole string and handles the syntax according to the function.

If we know what type and that the command is there, we need to split the string for syntax. The spliter "-" is the deference in syntax notes. This is taken care of through StringSplit and the syntax is stored as follows:

$Data[0] - Number of Parameters
$Data[1] - Name of Command
$Data[2] - Parameter 1
$Data[N] - Parameter (N - 1)

From here you must handle the parameters as you wish.

To help understanding, I will share a function called /name with you as an example.

Consider this function:

Case StringInStr($Data, "/name")
            $Data = StringSplit($Data, "-")
            $Names[$SocketNumber] = $Data[2]
            TCPSend($ConnectedSocket[$SocketNumber], "Server>>> Name set to " & $Data[2] & ".")

Off the bat, we can tell two things. This is a defined function(with syntax), and the name of the function is "/name".

Since this only has one parameter, the "name" is stored in $Data[2]. Now we can store this name in a variable to be called later and alert the user that the command has been performed.

$Names[$SocketNumber] = $Data[2]

The setup of this new array is as follows(placed at the beginning of script):

Global $Names[ ($MaxConnection + 1) ]
For $Track = 0 To $MaxConnection Step 1
    $Names[$Track] = "Socket " & $Track
Next

Now that the name is stored we must alert the user. This is more simple then thought. Since the $SocketNumber on which the command was sent is saved, you can contact them via the global $ConnectedSocket array.

TCPSend($ConnectedSocket[$SocketNumber], "Server>>> Name set to " & $Data[2] & ".")

That completes the command, however we are still not done. Locate the "Case Else" statement in StringAnalyze. This basically states that if no command is found, then send the message by default. The only thing you need to do is read the name of the original sender off the global $Names array to retransmit the message. This is shown by this:

TCPSendMessageAll($MaxConnection, $Names[$SocketNumber] & ">>>" & $Data)

Now you are finished!

I hope now that you understand writing functions inside this server, you might use it more.

AutoIt Smith

Edited by AutoIt Smith
Link to comment
Share on other sites

  • 8 months later...

OK first - WTF ?! i'm the first reply ?!?!?!?!?!

now we can go on ...

________

hello there, i love u'r scripts (that one and the "Server Communicator") they seem GREAT!

however i do have some questions )-:, but before i'd like to describe my doings.

________[project description:]_________

*- i'm making 2 client types and 2 server type files. (lets say Client A/B and Server A/B)

Client A: once started connects to Server A and sends it a picture, once done waits for string from Server A,

when srting recieved sends confirmation and exits.

Server A: waiting to recieve and save pictures from clients A types, once recieve a pic checks for

a certain text in a file, if text exists - sends it back to client A, waits for confirmation from client A and kicks him.

Server B: checks to see if there are any recieved pictures (saved by server A), if so sends them to Client B types,

waits for strings from client B types, once recieved writes them to the text file which Server A reads from and deletes

the pics.

Client B: recieves pictures from Server B and sends text accordingly.

________[My Questions:]_________

1. there is a line at the end of the 'main while' :

_PriorityQueueCheck($Queue, "StringAnalyze") oÝ÷ Ø:0z¯zÚ©rÍëazËkx,~íä.zç­£
+}ð«b~'hmÁ©í¶Ûazk ±¶Ú-ç%éí³}Âj}ý·£ºËiºÜ!jׯz·îËb¢{0{§¶Ú!jwezØ^­ç"z÷u«ZlRL@ýº¶­ªê-JÚâ'j³z«¨µû§s}Ó¡·Å§-­ø£ºËCÆëazÇ­ÂîËb¢{ ¶«¨µ+kx©rÍꮢ֧v«¨µ0IéÝ1ë,jZ®¢Öò¶­jëh×6Func StringAnalyze($String)
Return

2. i've read the functions regarding the queue line and it seems like the last msg is always written to the last location in the queue (#101).

- why isn't it written to #2 ?!

- how does the queue line work ?! what does it read first and what last ?! how does it write the queue (last / first ) ?!

- why do we need that queue ?! is it time-based to speed up connections ?!

- Please try to ELEBORATE on that queue and how it works (all these array calls within array calls confused me )-: )

Realy looking forward for any response -> THANKS in advance !

Edited by Armand

[u]My Au3 Scripts:[/u]____________(E)Lephant, A Share download manager (RS/MU etc)Http1.1 Console, The Ez Way!Internet Reconnection Automation Suite & A Macro Recording Tool.SK's Alarm Clock, Playing '.MP3 & .Wav' Files._________________Is GOD a mistake of the Humanity Or the Humanity is a mistake of GOD ?!

Link to comment
Share on other sites

First of all:

StringAnalyze is for any string based situation since the return value in the Queue is the received $String. Once called it evaluates however you want inside the function.

For example:

Client sends transmission "Hello!".
Server receives and logs as variable and sets to a priority list called Queue.
<other protocol>
(Once Queue is reached on specific item)
Variable(transmission string) is sent to the function {StringAnalyze} so it can be evaluated.

There for in that situation String would equal the original transmission "Hello!":

Func StringAnalyze($String)
Return $String
EndFunc

Please use StringSearch, StringLeft/Right, ETC..... to find the string you want out of the transmission and evalute as such.

; inside the StringAnalyze function.....
Select
Case StringLeft($String, 3) = "~ok"
; Everything is ok!
Case Else
; Nothing else matched.
EndSelect

Hopefully this makes sense.

-AutoIt Smith

Link to comment
Share on other sites

hey mate, I REALLY APPRICIATE YOUR REPLY !!!

but, it seems like you've merely answered any of my questions :shocked:

most of my questions can even be answered with a pure simple Yes/No answers and yet .... NONE :(

- looking forward for some "To-The-Point" answers instead of mini-tutorials which are quite nice but still not helpful :P

THANKS IN ADVANCE

(your the ;) ! )

ShlomiKalfa.

[u]My Au3 Scripts:[/u]____________(E)Lephant, A Share download manager (RS/MU etc)Http1.1 Console, The Ez Way!Internet Reconnection Automation Suite & A Macro Recording Tool.SK's Alarm Clock, Playing '.MP3 & .Wav' Files._________________Is GOD a mistake of the Humanity Or the Humanity is a mistake of GOD ?!

Link to comment
Share on other sites

Didnt have enough time for the second part....... not everyone has the world of time.

1. there is a line at the end of the 'main while' :

-Do we have to analyze the strings for the Queue to work ?!

A function must be called with a single variable.

what if i don't want to send the msgs back to the clients ?!

Then don't code it in the Analyzer.

-Can't we just put whatever functions we want to handle the recieved data INSTEAD of that "StringAnalyze" func ?!

Yes.

-To be exact, what if i just Disable these two functions -> "StringAnalyze" and "TCPSendMessageAll" by that:

Try trial and error. As long as StringAnalyze isnt being called, you don't need it. As long as TCPSendMsgAll isn't being called, etc....

2. i've read the functions regarding the queue line and it seems like the last msg is always written to the last location in the queue (#101).

- why isn't it written to #2 ?!

There isn't enough flow to write messages into a second line. If there were 20+ users connected then it would default into multipul lines.

- how does the queue line work ?!

Nwfu invented it, basically it saves the variables and timestamp of receipt and runs the function(StringAnalyze or any other single variable function) in the order it was received.

what does it read first and what last ?! how does it write the queue (last / first ) ?!

In the order it was received.

- why do we need that queue ?! is it time-based to speed up connections ?!

Time based to make sure there is no stack or overflow and to regulate messages.

- Please try to ELEBORATE on that queue and how it works (all these array calls within array calls confused me )-: )

Server receives things and writes them in a global variable. Then is processed. For more technical information contact nfwu.

-Smith

Link to comment
Share on other sites

  • 11 months later...

This is a long shot considering the last post is dated about a year ago, and I hope I don't get in trouble for bringing this back up, but I have a question regarding the syntax of the StringSplit in the case of "defined" command. You are use a hyphen (-) as a delimiter. So does this mean when the client sends a defined command the syntax would be /name-Colin? I would think you would want a space there, like /name Colin.

I guess it doesn't really matter in my case as I am hard coding the commands into the client. I'm not making any kind of chat program, I am specifically trying to send a set of data across to the server and I want each individual field to be sent over with the field name in front of it. I am using your concept of "commands" to do this. So I have around 8 fields on my GUI (Input and Combo boxes) and when the user presses the "Send" button, it should send each one of those field values across. I'm adding "/UserName " or "/Company " or whatever the field name is in front of the actual value in the input. Should I be adding "/UserName-" instead? Could I use a different delimiter?

I guess the only difference is that the delimiter can't be contained in the input that the user enters. Otherwise it will screw up the code (unless I can somehow escape it out but I don't know how to do that in the case of a non-code escape because I don't want to actually escape it from the code, I just don't want it to be used as a delimiter later on in the code). I guess the best thing would be to use the least likely character for someone to use as the delimiter and then remove it from the string by using StringReplace.

Let me know if anyone has a more elegant solution. Thanks.

Link to comment
Share on other sites

  • 2 weeks later...

I am using this script with my project, so let me answer this for him..

This is a long shot considering the last post is dated about a year ago, and I hope I don't get in trouble for bringing this back up, but I have a question regarding the syntax of the StringSplit in the case of "defined" command. You are use a hyphen (-) as a delimiter. So does this mean when the client sends a defined command the syntax would be /name-Colin? I would think you would want a space there, like /name Colin.

Yes, the command must bey /name-Name. I don't think it's good to use space as it is commonly used.

I guess it doesn't really matter in my case as I am hard coding the commands into the client. I'm not making any kind of chat program, I am specifically trying to send a set of data across to the server and I want each individual field to be sent over with the field name in front of it. I am using your concept of "commands" to do this. So I have around 8 fields on my GUI (Input and Combo boxes) and when the user presses the "Send" button, it should send each one of those field values across. I'm adding "/UserName " or "/Company " or whatever the field name is in front of the actual value in the input. Should I be adding "/UserName-" instead? Could I use a different delimiter?

Yes, it should be something like "/UserName-Colin".. You can use different delimtiter, but you must change the delimiter in the server side.. the one used in StringSplit function..

I guess the only difference is that the delimiter can't be contained in the input that the user enters. Otherwise it will screw up the code (unless I can somehow escape it out but I don't know how to do that in the case of a non-code escape because I don't want to actually escape it from the code, I just don't want it to be used as a delimiter later on in the code). I guess the best thing would be to use the least likely character for someone to use as the delimiter and then remove it from the string by using StringReplace.

Let me know if anyone has a more elegant solution. Thanks.

StringReplace is a good idea by doing it before sending the command to the server. Once a reply is received from the server return it back to its original character.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...