Building the Server
There are two components in this chat application: server and client. For the server, I will create a console application project using Visual Studio 2005 beta 2. Name the project "Server."
| Editor's Note: While we originally reported this project as suitable for Visual Studio 2003, we have learned that one line of code causes a problem in that version. We regret the error. |
In the default Module1.vb, populate it with the following:
Imports System.Net.Sockets
Module Module1
Const portNo As Integer = 500
Dim localAdd As System.Net.IPAddress = _
System.Net.IPAddress.Parse("10.0.1.4")
Dim listener As New _
System.Net.Sockets.TcpListener(localAdd, portNo)
Sub Main()
listener.Start()
While True
Dim user As New _
ChatClient(listener.AcceptTcpClient)
End While
End Sub
End Module
The next step is to define the ChatClient class. The ChatClient class is used to represent information from each client connecting to the server. Add a new Class to your project in Visual Studio 2005 and name it
ChatClient.vb.
First, import the following namespace:
Imports System.Net.Sockets
In the ChatClient class, first define the various private members (their uses are described in the comments in the code). You also declare a HashTable object (AllClients) to store a list of all clients connecting to the server. The reason for declaring it as a shared member is to ensure all instances of the ChatClient class are able to obtain a list of all the clients currently connected to the server:
'---class to contain information of each client
Public Class ChatClient
'---contains a list of all the clients
Public Shared AllClients As New Hashtable
'---information about the client
Private _client As TcpClient
Private _clientIP As String
Private _ClientNick As String
'---used for sending/receiving data
Private data() As Byte
When a client gets connected to the server, the server will create an instance of the ChatClient class and then pass the TcpClient variable (client) to the constructor of the class. You will also get the IP address of the client and use it as an index to identify the client in the HashTable object. The
BeginRead() method will begin an asynchronous read from the NetworkStream object (
_client.GetStream) in a separate thread. This allows the server to remain responsive and continue accepting new connections from other clients. When the reading is complete, control will be transferred to the
ReceiveMessage() function (which I will define shortly).
'---when a client is connected
Public Sub New(ByVal client As TcpClient)
_client = client
'---get the client IP address
_clientIP = client.Client.RemoteEndPoint.ToString
'---add the current client to the hash table
AllClients.Add(_clientIP, Me)
'---start reading data from the client in a
' separate thread
ReDim data(_client.ReceiveBufferSize - 1)
_client.GetStream.BeginRead(data, 0, _
CInt(_client.ReceiveBufferSize), _
AddressOf ReceiveMessage, Nothing)
End Sub
In the
ReceiveMessage() function, I first call the
EndRead() method to handle the end of an asynchronous read. Here, I check if the number of bytes read is less then 1. If it is, the client has disconnected and you need to remove the client from the HashTable object (using the IP address of the client as an index into the hash table). I also want to broadcast a message to all the clients telling them that this particular client has left the chat. I do this using the
Broadcast() function (again, I will define this shortly).
In this function, you check the various message formats sent from the client and take the appropriate action. For example, if the client initiates a FTP request, you need to repackage the message (as described in the earlier section "Protocol Description") and send it to the recipient. Listing 1 shows the full code for the ReceiveMessage() function.
The SendMessage() function allows the server to send a message to the client.
'---send the message to the client
Public Sub SendMessage(ByVal message As String)
Try
'---send the text
Dim ns As System.Net.Sockets.NetworkStream
SyncLock _client.GetStream
ns = _client.GetStream
Dim bytesToSend As Byte() = _
System.Text.Encoding. _
ASCII.GetBytes(message) _
ns.Write(bytesToSend, 0, _
bytesToSend.Length)
ns.Flush()
End SyncLock
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
Finally, the
Broadcast() function sends a message to all the clients stored in the
AllClients HashTable object.
'---broadcast message to selected users
Public Sub Broadcast(ByVal message As String, _
ByVal users() As String)
If users Is Nothing Then
'---broadcasting to everyone
Dim c As DictionaryEntry
For Each c In AllClients
'---broadcast message to all users
CType(c.Value, _
ChatClient).SendMessage(message & vbCrLf)
Next
Else
'---broadcasting to selected ones
Dim c As DictionaryEntry
For Each c In AllClients
Dim user As String
For Each user In users
If CType(c.Value, ChatClient). _
_ClientNick = user Then
'---send message to user
CType(c.Value, ChatClient). _
SendMessage(message & vbCrLf)
'---log it locally
Console.WriteLine("sending -----> " _
& message)
Exit For
End If
Next
Next
End If
End Sub