Results 1 to 1 of 1

Thread: client/server communication UPDATE

Threaded View

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Jan 2009
    Location
    Norway
    Posts
    185

    client/server communication UPDATE

    EDIT: Ok, I'm changing this question, as I've build my code out a lot. From looking at the 2004 Microsoft 101 Code Examples, I found some good hints. However I'm missing some understanding to what is actually happening..

    The program is managing client connections and processing commands between the server and the connected clients.
    My class structure looks like:
    Form
    Form - Program
    Form - Program - ClientSocket (Connection to single server)
    Form - Program - ServerSocket (Connection to multiple clients)
    Form - Program - ServerSocket - UserConnection
    Form - Program - ActiveUsers (Business layer object)

    I kind of need a way to bind the ActiveUser with the UserConnection. But I still feel it's appropriate to have the UserConnection as sub class of ServerSocket, and ActiveUser as sub class of Program (for BLL purposes). Is there a way I can make UserConnection a child/property of both ServerSocket and ActiveUsers, referencing to the same object? (I'm sure that is simple, but I have not learned this yet) Otherwise, does it look sensible?

    My other question is about the actual communication.. The way it looks to me, the DoListen method, has a loop that constantly is listening to new incoming data on the selected port. Also it seems to "have to" accept any data, regardless of connection. Whenever it gets data, it creates a NEW connection, and goes to the OnLineReceived method. This method could typically just accept/refuse based on login details, and subsequently add sender to the _userList. (?)

    However, it also seems (?) to me that any legitimate message received from an already included userConnection is ALSO handled by the same OnLineReceived method..? This seems strange to me.. What the stops an unauthorized new connection to send a command different than a "CONNECT" request, without it getting stopped? Could I fix this by changing the OnLineRecieved method as shown in bottom code? I feel uncertain, as it was strange to me why the MS example doesn't seem to make this discretion, and hence seems open to unauthorized message processing.. (?)

    So is it just me who have misunderstood completely how this works...?


    Code:
     Public Class ServerSocket
    
        Const PORT_NUM As Integer = 1111
    
        Private _localIp As Net.IPAddress
        Private _IpEndPoint As Net.IPEndPoint
        Private _listener As TcpListener
        Private _port As Integer
        Private _listenerThread As Threading.Thread
    
        Private _userList As New List(Of UserConnection)           
        Private _user As UserConnection
    
        Public Sub New(ByVal port As Integer)
            _port = port
            Dim host As Net.IPHostEntry
            host = Dns.GetHostEntry("127.0.0.1")
            _localIp = host.AddressList.GetValue(0)
            _listener = New TcpListener(_localIp, _port)
        End Sub
    
        Public Sub OpenConnection()
            _listenerThread = New Threading.Thread(AddressOf DoListen)
            _listenerThread.Start()
        End Sub
    
        ' This subroutine is used as a background listener thread to allow reading incoming
        ' messages without lagging the user interface.
        Private Sub DoListen()
            Try
                ' Listen for new connections.
                _listener = New TcpListener(System.Net.IPAddress.Any, PORT_NUM)
                _listener.Start()
                Do
                    ' Create a new user connection using TcpClient returned by
                    ' TcpListener.AcceptTcpClient()
                    Dim client As New UserConnection(_listener.AcceptTcpClient)
    
                    ' Create an event handler to allow the UserConnection to communicate
                    ' with the window.
                    AddHandler client.LineReceived, AddressOf OnLineReceived
                    UpdateStatus("New connection found: waiting for log-in")
                Loop Until False
            Catch
            End Try
        End Sub
    
        ' This subroutine checks to see if username already exists.  If it does, send a 
        ' REFUSE message, otherwise confirm with a JOIN.
        Private Sub ConnectUser(ByVal userName As String, ByVal sender As UserConnection)
            If _userList.Contains(sender) Then
                ReplyToSender("REFUSE", sender)
            Else
                sender.Name = userName
                _userList.Add(sender)
    
                ' Send a JOIN to sender
                ReplyToSender("JOIN", sender)
            End If
        End Sub
    
        ' This subroutine removes the user
        Private Sub DisconnectUser(ByVal sender As UserConnection)
            ' listbox message
            _userList.Remove(sender)
        End Sub
    
        ' This is the event handler for the UserConnection when it receives a full line.
        ' Parse the cammand and parameters and take appropriate action.
        Private Sub OnLineReceived(ByVal sender As UserConnection, ByVal data As String)
            Dim dataArray() As String
    
            ' Message parts are divided by "|"  Break the string into an array accordingly.
            dataArray = data.Split(Chr(124))
    
            ' dataArray(0) is the command.
            Select Case dataArray(0)
                Case "CONNECT"
                    If ValidateUserLogon(dataArray(1), dataArray(2)) Then
                        ConnectUser(dataArray(1), sender)
                    End If
                Case "ORDER"
                Case "DISCONNECT"
                    DisconnectUser(sender)
                Case Else
                    UpdateStatus("Unknown message:" & data)
            End Select
        End Sub
    
        ' This subroutine sends a response to the sender.
        Private Sub ReplyToSender(ByVal strMessage As String, ByVal sender As UserConnection)
            sender.SendData(strMessage)
        End Sub
    
        Private Function ValidateUserLogon(ByVal userName As String, ByVal psw As String) As Boolean
            ' To-do:  make logon logic
            Return True
        End Function
    
    .....
    
    End Class
    Code:
    Option Strict On
    
    Imports System.Net.Sockets
    Imports System.Text
    
    ' The UserConnection class encapsulates the functionality of a TcpClient connection
    ' with streaming for a single user.
    Public Class UserConnection
        Const READ_BUFFER_SIZE As Integer = 255
    
        Private _client As TcpClient
        Private _readBuffer(READ_BUFFER_SIZE) As Byte
        Private _strName As String
    
    #Region "Events"
        Public Event LineReceived(ByVal sender As UserConnection, ByVal Data As String)
    #End Region
    
    #Region "Constructors"
        ' Overload the New operator to set up a read thread.
        Public Sub New(ByVal client As TcpClient)
            Me._client = client
            ' This starts the asynchronous read thread.  The data will be saved into readBuffer.
            Me._client.GetStream.BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, AddressOf StreamReceiver, Nothing)
        End Sub
    #End Region
    
    #Region "Properties"
        ' The Name property uniquely identifies the user connection.
        Public Property Name() As String
            Get
                Return _strName
            End Get
            Set(ByVal Value As String)
                _strName = Value
            End Set
        End Property
    #End Region
    
        ' This subroutine uses a StreamWriter to send a message to the user.
        Public Sub SendData(ByVal Data As String)
            ' Synclock ensure that no other threads try to use the stream at the same time.
            SyncLock _client.GetStream
                Dim writer As New IO.StreamWriter(_client.GetStream)
                writer.Write(Data & Chr(13) & Chr(10))
    
                ' Make sure all data is sent now.
                writer.Flush()
            End SyncLock
        End Sub
    
        ' This is the callback function for TcpClient.GetStream.Begin. It begins an 
        ' asynchronous read from a stream.
        Private Sub StreamReceiver(ByVal ar As IAsyncResult)
            Dim BytesRead As Integer
            Dim strMessage As String
    
            Try
                ' Ensure that no other threads try to use the stream at the same time.
                SyncLock _client.GetStream
                    ' Finish asynchronous read into readBuffer and get number of bytes read.
                    BytesRead = _client.GetStream.EndRead(ar)
                End SyncLock
    
                ' Convert the byte array the message was saved into, minus one for the
                ' Chr(13).
                strMessage = Encoding.ASCII.GetString(_readBuffer, 0, BytesRead - 1)
                RaiseEvent LineReceived(Me, strMessage)
    
                ' Ensure that no other threads try to use the stream at the same time.
                SyncLock _client.GetStream
                    ' Start a new asynchronous read into readBuffer.
                    _client.GetStream.BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, AddressOf StreamReceiver, Nothing)
                End SyncLock
            Catch e As Exception
            End Try
        End Sub
    End Class
    Code:
      ' This is the event handler for the UserConnection when it receives a full line.
        ' Parse the cammand and parameters and take appropriate action.
        Private Sub OnLineReceived(ByVal sender As UserConnection, ByVal data As String)
            Dim dataArray() As String
    
            ' Message parts are divided by "|"  Break the string into an array accordingly.
            dataArray = data.Split(Chr(124))
    
            If _userList.Contains(sender) = False Then
                Select Case dataArray(0)
                    Case "CONNECT"
                        If ValidateUserLogon(dataArray(1), dataArray(2)) Then
                            ConnectUser(dataArray(1), sender)
                        End If
                    Case Else
                        ReplyToSender("REFUSED", sender)
                End Select
    
            Else
                Select Case dataArray(0)
                    Case "ORDER"
                    Case "DISCONNECT"
                        DisconnectUser(sender)
                    Case Else
                        UpdateStatus("Unknown message:" & data)
                End Select
            End If
        End Sub
    Last edited by bretddog; Apr 5th, 2010 at 08:28 AM. Reason: Changed unanswered question

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width