Jump to content

TCP multiconnections problem


Recommended Posts

Hello, I am making a simple Instant messenger and I have made the client without a problem. The server I'm having a bit of trouble with. I have made a server that can allow one person to connect and chat with me but I'm trying to get it so multiple people can connect. I have written some of the code for it but I am stuck.

TCPStartup()

Global $SERVER, $SOCKET

$i = 0

Dim $SOCKET[?]

$SERVER = TCPListen(@IPAddress1,1337)
If @error Then
    MsgBox("Error to listening on port 1337.")
    Quit()
EndIf



While 1
    sleep(10)
    
    $TConn = TCPAccept($SERVER)
    
    If $TConn <> -1 Then
        $SOCKET[$i] = $TConn
        $i += 1
    EndIf
    
    For $Cx = 0 to $i
        $RECV = TCPRecv($SOCKET[$i], 512)
        If $RECV <> "" Then
            sendall()
        EndIf
    Next

WEnd

Func sendall()
    $DATA = $RECV
    If $DATA <> "" Then
        For $Cx = 0 to $i
            TCPSend($SOCKET[$Cx], $DATA)
        Next
    EndIf
EndFunc

Func Quit()
    For $Cx = 0 to $i
    TCPCloseSocket($SOCKET[$Cx])
    Next
    TCPShutdown()
    Exit
EndFunc

I don't know how to use arrays very well so this is a problem for me. If anyone could help in any way like push me in the right direction or help me out with the arrays I'd be thankful. Thanks in adv.

Link to comment
Share on other sites

I made a few tweaks to your code, enough I think to get it basically functional. In this kind of application, I would prefer the array UDF functions, as I did below (ie: _arrayAdd and _arrayDelete ).

Looking through the help doc on a few of the functions I added should be a decent starting point. And feel free to ask if you have any particular questions.

#include <Array.au3>

TCPStartup()

Global $SERVER, $SOCKET[1]

$SERVER = TCPListen(@IPAddress1,1337)
If @error Then
    MsgBox(0,"error","Error to listening on port 1337.")
    Quit()
EndIf

While 1
    sleep(25)
    
    $TConn = TCPAccept($SERVER)
    
    If $TConn <> -1 Then
        _ArrayAdd($SOCKET,$TConn)
    EndIf
    
    For $Cx = 0 to UBound($SOCKET) - 1
        If $SOCKET[$Cx] <> "" Then
            $RECV = TCPRecv($SOCKET[$Cx], 512)
            If $RECV <> "" Then
                sendall($RECV)
            EndIf
        EndIf
    Next

WEnd

Func sendall($msg)
    If $msg <> "" Then
        For $Ox = 0 to UBound($SOCKET) - 1
            If $SOCKET[$Ox] <> "" Then
                If TCPSend($SOCKET[$Ox], $msg) == 0 And @error Then
                    TCPCloseSocket($SOCKET[$Ox])
                    _ArrayDelete($SOCKET,$Ox)
                EndIf
            EndIf
        Next
    EndIf
EndFunc

Func Quit()
    For $Qx = 0 to UBound($SOCKET) - 1
        TCPCloseSocket($SOCKET[$Qx])
    Next
    TCPShutdown()
    Exit
EndFunc
Edited by bwochinski
Link to comment
Share on other sites

Thank you so much =]. I suck at arrays and this is practically the first time I have tried TCP. Would you like to help me test this? I can upload the client.

I would (with source anyway), but the network I'm on right now is heavily fire-walled, so the chances of my being able to connect to anything are about nil.

For a simple test yourself you should be able to just run 2 instances of the client and see your messages propagate from one to the other.

Link to comment
Share on other sites

hi, i started with TCP about a week ago, i had the same problems

i suggest to look at this example:

the $0, $00 variables are quite confusing, but everything else is clear and well-commente.

that's where i started

gigi1

Link to comment
Share on other sites

Thanks Gigi1, I do have another problem now. I don't know how to have people "disconnect" from the server. I need it to close their socket on the server side. I am using the server that bwochinski has helped me with. The way I was thinking about trying would close every socket and I don't know how I would target just the one socket that was being closed.

Link to comment
Share on other sites

hi, you need to have client's sockets store in different variables. i use a 2 dimensional array to make everything easier (for me at least :))

here's a small example of how my server is structured

note that it will NOT work alone, there are some undefined user functions ("out" stamps to console, "ProcessData"... processes data :))

;Socket[0][0] contains the number of connected clients
    ;Socket[n][0] contains the Main connection socket identifier of the client n
    ;Socket[n][1] contains the Public IP of the client
    ;Socket[n][2] contains the Local IP of the client
    ;Socket[n][3] contains the computer name of the client
    ;Socket[n][4] contains the ping timer of the client
    ;Socket[n][5] contains the Connection timer of the client
    ;Socket[n][6] contains the connection status of the client: 0 means offline, 1 means connected

While 1
    Sleep(10)

    AcceptNewConnection()

    If ReceiveConnection() = 1 Then ProcessData()
WEnd

;Func AcceptNewConnection()
;calls to TCPAccept, the processes request.
;Returns 1 if a new client connected, returns 0 otherwise.
Func AcceptNewConnection()
    Local $TCPAccept = TCPAccept($ListeningSocket)
    If $Socket[0][0] = $MaxClients Then
        Out("A client tryed to connect, but client max number has beed reached")
        TCPCloseSocket($TCPAccept)
        Return 0
    EndIf
    If $TCPAccept = -1 Then Return 0                        ;no new connection -> return
    For $client_processing=1 To $MaxClients                 ;find the first open socket, assign new client to that socket
        If $Socket[$client_processing][0] = 0 Then
            $Socket[$client_processing][0] = $TCPAccept
            $Socket[0][0] = $Socket[0][0] + 1
            Out("New Client Connected - assigned on Client "&$client_processing)
            Return 1
        EndIf
    Next
EndFunc

;Func ReceiveConnection()
;calls to TCPRecv for every connected client, checks connection if there are errors
;Returns 1 if some data is received, returns 0 otherwise
Func ReceiveConnection()
    For $client_processing=1 To $MaxClients                                 ;receive connection for every client (even disconnected ones)
        $ReceivedData = TCPRecv($Socket[$client_processing][0], 1000000)    ;TCPRecv
        If @error <> 0 And $Socket[$client_processing][6] = 1 Then          ;if there's an error (=no active socket) then close that
            TCPCloseSocket($Socket[$client_processing][0]                   ;close socket
            For $i=0 To 6 Step 1                                            ;reset the array of the client processing
                $Socket[$client_processing][$i]=0
            Next
            Return -1
        EndIf
        If $ReceivedData <> "" Then                                         ;if $ReceivedData actually contains something then execute the ProcessData func
            Return 1
        EndIf
    Next
    Return 0
EndFunc

EDIT: i saw the server by bwochinsky, you can add a check for error just after TCPRecv, if there are errors connection is inactive, and you can close the socket

Edited by gigi1
Link to comment
Share on other sites

EDIT: i saw the server by bwochinsky, you can add a check for error just after TCPRecv, if there are errors connection is inactive, and you can close the socket

Good point, I didn't add a check on the TCPRecv function. I only had bad sockets thrown out after the TCPSend failed in the "sendall()" function.

If you're wanting to really make this a more robust server, you'll need to implement some sort of chat protocol where you can send messages from the client to the server about things like connections, disconnections, chat messages, and user nicknames. A good exercise along this line might be to create a basic IRC server, since the IRC protocol is pretty simple itself but very useful.

Link to comment
Share on other sites

Good point, I didn't add a check on the TCPRecv function. I only had bad sockets thrown out after the TCPSend failed in the "sendall()" function.

If you're wanting to really make this a more robust server, you'll need to implement some sort of chat protocol where you can send messages from the client to the server about things like connections, disconnections, chat messages, and user nicknames. A good exercise along this line might be to create a basic IRC server, since the IRC protocol is pretty simple itself but very useful.

I already have something like that.

Client:

#Include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <WindowsConstants.au3>
#include <ButtonConstants.au3>
#include <Misc.au3>


Global $username = InputBox("Username", "What would you like your username to be?")

$login = 0

TCPStartup()

Global $CONNECT

$CONNECT = TCPConnect("" ,1337)
If @error Then
    MsgBox(16, "Error", "Cannot connect to '' on port 1337.")
    Quit()
EndIf

GUICreate("Donald's Instant Messenger", 300, 300)
GUISetState()

Global $info = GUICtrlCreateEdit("", 10, 10, 280, 200, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL), $WS_EX_STATICEDGE)
GUICtrlSetBkColor(-1, 0xFFFFFF)

Global $sending = GUICtrlCreateInput("", 10, 220, 200, 70)
$b_send = GUICtrlCreateButton("Send", 220, 220, 70, 70, BitOR($BS_DEFPUSHBUTTON, $WS_GROUP))

TCPSend($CONNECT, "[" & @Hour & ":" & @MIN & ":" & @SEC & "] " & $username & " has connected.")

While True
    $RECV = TCPRecv($CONNECT,512)
    If @error Then Quit()
    
    If $RECV <> "" Then
        GUICtrlSetData($info, $RECV & @CRLF, True)
    EndIf
    
    If $RECV = "MSG~SHUTDOWN" Then
        GUIDelete()
        MsgBox(0, "Shutting down", "The server has shut down.")
        Exit
    EndIf
    
    $msg = GUIGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
        Quit()
    Case $msg = $b_send
        say()
    EndSelect
    
    
WEnd


Func say()
    $DATA = GUICtrlRead($Sending)
    If $DATA <> "" Then
        TCPSend($CONNECT, "[" & @Hour & ":" & @MIN & ":" & @SEC & "] " & $username & ": " & $DATA)
        GUICtrlSetData($sending, "")
    EndIf
EndFunc

Func Quit()
    TCPSend($CONNECT, "[" & @Hour & ":" & @MIN & ":" & @SEC & "] " & $username & " has disconnected.")
    sleep(50)
    TCPCloseSocket($CONNECT) ; Close socket
    TCPShutdown()
    Exit
EndFunc
Edited by Donald8282
Link to comment
Share on other sites

That's a start, but the issue with implementing that client side is that I can kick everyone off your server with this script:

$CONNECT = TCPConnect("your server ip" ,1337)

While 1

    TCPSend($CONNECT,"MSG~SHUTDOWN")
    Sleep(5000)

WEnd

Oh, yeah... I forgot to take that out. I was testing that with something else. Thanks for showing me that.
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...