|
-
Apr 4th, 2010, 06:09 AM
#1
Thread Starter
Addicted Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|