Jump to content

TCP Script Problem


Recommended Posts

Hey,

I'm having a problem with my multi-user TCP chat program. When a user tries to connect, the server accepts the connection (as far as I can tell), but will not receive anything after that (or not the first thing sent anyway). I started getting an array error on line 149 (_TCPRecv was only returning an empty string rather than an array), so I thought that maybe it was just being too quick and hadn't received the first thing the client sends yet. I then created _SockRecv to keep listening until it gets this request before returning anything, but it still doesn't seem to receive anything and gets stuuck in an infinite loop.

Any ideas on how to fix this?

Thanks

Server:

;Constants
Global Const $IP = @IPADDRESS1
Global Const $PORT = 32768

;Variables
Local $i
Local $u
Local $receive[3]
Local $sock
Local $header
Local $max
Local $data
Local $from
Global $MainSocket ; TCPListen returns main socket identifier to this variable
Global $SETTINGSlog = 1 ;If 1, log file of messages will be written
Global $logfile ;Filehandle of log file
Global $maxconnections = 15 ;maximum number of clients minus 1 (so index of $maxconnections is last connection, not second last; more efficient)
Global $users[$maxconnections+2][2] ;[index][socket,username]

;Create log file
If $SETTINGSlog = 1 Then
    $logfile = FileOpen(@ScriptDir&"\log_system.txt",1)
    FileWriteLine($logfile,"##### STARTING SERVER AT "&@MDAY&"/"&@MON&"/"&@YEAR&" "&@HOUR&":"&@MIN&" WITH IP "&$IP&" AND PORT "&$PORT&" #####")
EndIf

;Starts TCP - If start fails, it will retry 4 times (5 attempt in total)
$i=0
Do
    $u = TCPStartup()
    If $u <> 1 Then
        If $i=4 Then Exit
        $i+=1
        _TrayTip("Basic TCP Server","Could not start up TCP. Retry "&$i&"/4",10,3)
    EndIf
Until $u = 1
_TrayTip("Basic TCP Server","TCP Startup complete.",10,1)

;Creates listening socket - same structure as TCPStartup function
$i=0
Do
    $MainSocket = TCPListen($IP,$PORT,16)
    If $MainSocket = -1 Then
        If $i=4 Then Exit
        $i+=1
        _TrayTip("Basic TCP Server","Could not create listening socket. Retry "&$i&"/4",10,3)
    EndIf
Until $MainSocket <> -1
_TrayTip("Basic TCP Server","Creating listening socket complete.",10,1)

;MAIN LOOP
While 1
    _CheckConnection()
    $socket = TCPAccept($MainSocket)
    If $socket <> -1 Then
        _AddConnection($socket)
    EndIf
    Sleep(300)
WEnd

;Passes parameters to regular TrayTip function, but also logs to file, if logsystem option is set to 1
Func _TrayTip($title="Basic TCP Chat",$text="",$timeout=10,$icon=0)
    TrayTip($title,$text,$timeout,$icon)
    If $SETTINGSlog = 1 Then
        FileWriteLine($logfile,"["&@MDAY&"/"&@MON&"/"&@YEAR&" "&@HOUR&":"&@MIN&"] "&$title&" | "&$text)
    EndIf
EndFunc

;Next two functions add custom defaults and extra processing to TCPRecv and TCPSend functions, respectively
Func _TCPRecv($sock,$max=2048)
    Return _DataFromTCP(TCPRecv($sock,$max))
EndFunc

Func _TCPSend($sock,$header,$data="")
    Return TCPSend($sock,_DataToTCP($header,$data))
EndFunc

Func _SockRecv($sock,$max=2048)
    $data = ""
    While $data = ""
        $data = TCPRecv($MainSocket,2048)
        If @error Then Return SetError(1,0,0)
    WEnd
    MsgBox(4096,"",$data);for debugging - so i can see when While loop ends
    Return _DataFromTCP($data)
EndFunc

Func _CheckConnection()
    For $i = 0 to $maxconnections
        If $users[$i][0] Then
            $receive = _TCPRecv($users[$i][1])
            If @error Then
                _DelConnection($i)
            Else
                If $receive[0] Then
                    Switch $receive[0]
                        Case "message"
                            _SendAll($receive,$users[$i][1],"message")
                        Case "disconnect"
                            _DelConnection($i)
                        Case Else
                            _TCPSend($sock,"invalid","Invalid header.")
                    EndSwitch
                EndIf
            EndIf
        EndIf
    Next
EndFunc

Func _AddConnection($sock)
    $receive = _SockRecv($sock)
    For $i = 0 to $maxconnections
        If Not $users[$i][0] Then
            If $receive[0] <> "connect" Then
                If $receive[1] <> "SYSTEM" Then
                    $users[$i][0] = $sock
                    If StringLen($receive[1]) > 16 Then
                        $users[$i][1] = StringLeft($receive[1],16)
                    ElseIf StringLen($receive[1]) <> 0 Then
                        $users[$i][1] = $receive[1]
                    Else
                        _TCPSend($sock,"rejected","No username entered.");header contains rejection, data contains reason
                        TCPCloseSocket($sock)
                        Return 0
                    EndIf
                    _TCPSend($sock,"accepted");Reply with accepted message
                    _SendUserlist($sock);Send the connected users list to new client - Function does not exist - Parameters:Socket to send to
                    _SendAll($receive[1],"SYSTEM","useradd");Send data to everyone - Function does not exist - Parameters:Data,From(optional),Header(optional) - If header is blank, default of "message" is used. If header is a system command, from parameter is ignored
                    Return 1
                Else
                    _TCPSend($sock,"rejected","Username ""SYSTEM"" is not allowed.");see above. needs changed. header contains rejection, data contains reason
                    TCPCloseSocket($sock)
                    Return 0
                EndIf
            Else
                _TCPSend($sock,"rejected","Incorrect connect request.");see above. needs changed. header contains rejection, data contains reason
                TCPCloseSocket($sock)
                Return 0
            EndIf
        EndIf
    Next
    _TCPSend($sock,"rejected","Server is full.");see above. needs changed. header contains rejection, data contains reason
    TCPCloseSocket($sock)
    Return 0
EndFunc

Func _DelConnection($i)
    TCPCloseSocket($users[$i][0])
    _SendAll($users[$i][1],"SYSTEM","deluser")
    $users[$i][0] = 0
    $users[$i][1] = ""
EndFunc

Func _SendAll($data,$from="SYSTEM",$header="message")
    For $i = 0 to $maxconnections
        If $users[$i][0] And $users[$i][1] <> $from Then
            _TCPSend($users[$i][0],$header,$from&" > "&$data)
        EndIf
    Next
EndFunc

Func _SendUserlist($sock)
    For $i = 0 to $maxconnections
        If $users[$i][0] Then _TCPSend($sock,"adduser",$users[$i][1])
    Next
EndFunc

;Next two functions prepare data to be sent over TCP, and that have been received over TCP, respectively
Func _DataToTCP($header,$data,$max=2048)
    If IsString($header) = 0 Then
        $header = String($header)
    EndIf
    If IsString($data) = 0 Then
        $data = String($data)
    EndIf
    $header = StringReplace($header,chr(1),"")
    $header = StringReplace($header,chr(2),"")
    $header = StringReplace($header,chr(3),"")
    $data = StringReplace($data,chr(1),"")
    $data = StringReplace($data,chr(2),"")
    $data = StringReplace($data,chr(3),"")
    If StringLen($data)>2048 Then
        $data = StringLeft($data,$max) ;If body is longer than $max bytes/characters, cut it down to length. Not the best solution, but works for testing. Client can be written around limitation, and make it more user friendly.
    EndIf
    Return chr(1)&$header&chr(2)&$data&chr(3)
EndFunc

Func _DataFromTCP($data) ;returns array containing header in index-0, and body/data in index-1
    If StringLeft($data,1) = chr(1) Then $data = StringTrimLeft($data,1)
    If StringRight($data,1) = chr(3) Then $data = StringTrimRight($data,1)
    Return StringSplit($data,chr(2),2)
EndFunc

Client:

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>

;Constants
Global Const $PORT = 32768

;Variables
Local $i
Local $user
Local $close
Global $loop = True
Global $maxusers = 15 ;maximum number of clients minus 1 (so index of $maxusers is last connection, not second last; more efficient)
Global $users[$maxusers+1][2] ;[index][username,ctrl id]
Global $socket
Global $WindowMain

;Starts TCP - If start fails, it will retry 4 times (5 attempt in total)
$i=0
Do
    $u = TCPStartup()
    If $u <> 1 Then
        If $i=4 Then Exit
        $i+=1
        _TrayTip("Basic TCP Client","Could not start up TCP. Retry "&$i&"/4",10,3)
    EndIf
Until $u = 1

Opt("GUIOnEventMode", 1)
$WindowLogin = GUICreate("Login", 218, 114, -1, -1)
$LabelServerIP = GUICtrlCreateLabel("Server IP", 16, 18, 48, 17)
$InputServerIP = GUICtrlCreateInput(@IPADDRESS1, 72, 16, 130, 21)
$LabelUsername = GUICtrlCreateLabel("Username", 16, 50, 52, 17)
$InputUsername = GUICtrlCreateInput("", 72, 48, 130, 21)
$ButtonLogin = GUICtrlCreateButton("Login", 128, 80, 75, 22, $WS_GROUP)

GUICtrlSetLimit($InputUsername,16)
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
GUICtrlSetOnEvent($ButtonLogin, "_Login")
GUISetState(@SW_SHOW)

While $loop
    Sleep(100)
WEnd

$WindowMain = GUICreate("Client - "&$username, 450, 214, -1, -1)
$EditMessages = GUICtrlCreateEdit("", 8, 8, 313, 169, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_READONLY,$ES_WANTRETURN,$WS_HSCROLL,$WS_VSCROLL))
$InputMessage = GUICtrlCreateInput("", 8, 184, 265, 21)
$ButtonSend = GUICtrlCreateButton("Send", 272, 183, 49, 22, $WS_GROUP)
$ListUsers = GUICtrlCreateListView("Users", 328, 8, 113, 196)
GUICtrlSendMsg($ListUsers, $LVM_SETCOLUMNWIDTH, 0, 80)

GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
GUICtrlSetOnEvent($ButtonSend, "_Send")
GUICtrlSetState($InputMessage,$GUI_FOCUS)
GUISetState(@SW_SHOW)

While 1
    Sleep(100)
    $receive = _TCPRecv()
    If @error Then
        _TrayTip("Basic TCP Client","An error has occured with TCP Receive and the application will close.",5,3)
        Sleep(5000)
        _Exit(1)
    Else
        If $receive[0] Then
            Switch $receive[0]
                Case "message"
                    GUICtrlSetData($EditMessages,$receive[1]&@CRLF,1)
                Case "useradd"
                    _UserAdd($receive[1])
                Case "userdel"
                    _UserDelete($receive[1])
                Case "accepted"
                    _TrayTip("Basic TCP Client","Connection request has been accepted. You are now connected to the server.",5,3)
                Case "rejected"
                    _TrayTip("Basic TCP Client","Connection request has been rejected. The application will close.",5,3)
                    Sleep(5000)
                    _Exit(1)
                Case "disconnect"
                    _TrayTip("Basic TCP Client","You have been disconnected from the server. The application will close.",5,3)
                    Sleep(5000)
                    _Exit(1)
                Case "invalid"
                    ;error
            EndSwitch
        EndIf
    EndIf
WEnd

;Passes parameters to regular TrayTip function, but also logs to file, if logsystem option is set to 1
Func _TrayTip($title="Basic TCP Chat",$text="",$timeout=10,$icon=0)
    TrayTip($title,$text,$timeout,$icon)
EndFunc

;Next two functions add custom defaults and extra processing to TCPRecv and TCPSend functions, respectively
Func _TCPRecv($max=2048)
    Return _DataFromTCP(TCPRecv($socket,$max))
EndFunc

Func _TCPSend($header="message",$data="")
    Return TCPSend($socket,_DataToTCP($header,$data))
EndFunc

Func _Send() ;Sends message (whereas _TCPSend sends anything)
    If GUICtrlRead($InputMessage) Then
        _TCPSend("message",GUICtrlRead($InputMessage))
        GUICtrlSetData($InputMessage,"")
        Return 1
    Else
        Return 0
    EndIf
EndFunc

Func _UserAdd($user)
    For $i = 0 to $maxusers
        If Not $users[$i][0] Then
            $users[$i][0] = $user
            $users[$i][1] = GUICtrlCreateListViewItem($user,$ListUsers)
            Return 1
        EndIf
    Next
    Return 0
EndFunc

Func _UserDelete($user)
    For $i = 0 to $maxusers
        If $users[$i][0] = $user Then
            $users[$i][0] = ""
            GUICtrlDelete($users[$i][1])
            $users[$i][1] = 0
            Return 1
        EndIf
    Next
    Return 0
EndFunc

Func _Login()
    GUICtrlSetState($ButtonLogin,$GUI_DISABLE)
    Global Const $IP = GUICtrlRead($InputServerIP)
    Global Const $username = GUICtrlRead($InputUsername)
    $i=0
    Do
        $socket = TCPConnect($IP,$PORT)
        If $socket = -1 Then
            If $i=4 Then _Exit()
            $i+=1
            _TrayTip("Basic TCP Client","Could not connect to server. Retry "&$i&"/4",10,3)
        EndIf
    Until $socket <> -1
    _TCPSend("connect",$username)
    $loop = False
    GUIDelete($WindowLogin)
EndFunc

Func _Exit($close=0)
    If @GUI_CtrlId = $GUI_EVENT_CLOSE And @GUI_WinHandle = $WindowMain Or $close Then TCPCloseSocket($socket)
    TCPShutdown()
    Exit
EndFunc

;Next two functions prepare data to be sent over TCP, and that have been received over TCP, respectively
Func _DataToTCP($header,$data,$max=2048)
    If IsString($header) = 0 Then
        $header = String($header)
    EndIf
    If IsString($data) = 0 Then
        $data = String($data)
    EndIf
    $header = StringReplace($header,chr(1),"")
    $header = StringReplace($header,chr(2),"")
    $header = StringReplace($header,chr(3),"")
    $data = StringReplace($data,chr(1),"")
    $data = StringReplace($data,chr(2),"")
    $data = StringReplace($data,chr(3),"")
    If StringLen($data)>2048 Then
        $data = StringLeft($data,$max) ;If body is longer than $max bytes/characters, cut it down to length. Not the best solution, but works for testing. Client can be written around limitation, and make it more user friendly.
    EndIf
    Return chr(1)&$header&chr(2)&$data&chr(3)
EndFunc

Func _DataFromTCP($data) ;returns array containing header in index-0, and body/data in index-1
    If StringLeft($data,1) = chr(1) Then $data = StringTrimLeft($data,1)
    If StringRight($data,1) = chr(3) Then $data = StringTrimRight($data,1)
    Return StringSplit($data,chr(2),2)
EndFunc
Edited by rabbitkillrun2
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...