rabbitkillrun2 Posted January 10, 2010 Share Posted January 10, 2010 (edited) 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: expandcollapse popup;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: expandcollapse popup#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 January 15, 2010 by rabbitkillrun2 Link to comment Share on other sites More sharing options...
darkjohn20 Posted January 10, 2010 Share Posted January 10, 2010 Look at some of his examples: http://www.autoitscript.com/forum/index.php?showuser=41162 Link to comment Share on other sites More sharing options...
rabbitkillrun2 Posted January 15, 2010 Author Share Posted January 15, 2010 Thanks.I looked at his example one, but I do not see any functional differences in our scripts (up to the point mine is getting stuck), so I still don't know what I've done wrong. Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now