Building the Client
Now that the server is built, it is time to build the client. Using Visual Studio 2005, I create a new Windows application (name it WinClient) and populate the default form with the controls shown in Table 1 (see the resulting form in
Figure 2).
Table 1. Client Controls
| Control(Name)
|
Property
|
Value
|
| Label |
Text |
Nick |
| TextBox |
Name |
txtNick |
| TextBox |
Name |
txtMessageHistory |
| |
MultiLine |
True |
| |
ReadOnly |
True |
| TextBox |
Name |
txtMessage |
| Button |
Name |
btnSignIn |
| |
Text |
Sign In |
| Button |
Name |
btnSend |
| |
Text |
Send |
The client application logic is very similar to the server, albeit more straightforward. Double-click on the form to switch to the edit window, and import the following namespace:
Imports System.Net.Sockets
Define the following constant and variables within the form:
Const portNo As Integer = 500
Dim client As TcpClient
Dim data() As Byte
 | |
| Figure 2. Adding Controls: Populate the Windows Form with the various controls from Table 1. |
When the user signs in, the client first connects to the server and sends the nickname of the user using the
SendMessage() subroutine (defined shortly). Then it should begin reading data from the server asynchronously and change the name of the Sign In button to "Sign Out." When the user signs out from the chat application, you invoke the
Disconnect() subroutine (defined shortly).
Private Sub btnSignIn_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnSignIn.Click
If btnSignIn.Text = "Sign In" Then
Try
'---connect to server
client = New TcpClient
client.Connect("127.0.0.1", portNo)
ReDim data(client.ReceiveBufferSize)
SendMessage(txtNick.Text)
'---read from server
client.GetStream.BeginRead( _
data, 0, _
CInt(client.ReceiveBufferSize), _
AddressOf ReceiveMessage, Nothing)
btnSignIn.Text = "Sign Out"
btnSend.Enabled = True
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Else
'---disconnect from server
Disconnect()
btnSignIn.Text = "Sign In"
btnSend.Enabled = False
End If
End Sub
When the user clicks on the Send button, the application sends a message to the server:
Private Sub btnSend_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnSend.Click
SendMessage(txtMessage.Text)
txtMessage.Clear()
End Sub
The
SendMessage() subroutine, used in the code above, allows the client to send a message to the server:
Public Sub SendMessage(ByVal message As String)
Try
'---send a message to the server
Dim ns As NetworkStream = client.GetStream
Dim data As Byte() = _
System.Text.Encoding.ASCII.GetBytes(message)
'---send the text---
ns.Write(data, 0, data.Length)
ns.Flush()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
The
ReceiveMessage() subroutine asynchronously reads data sent from the server in a separate thread. When the data is received, it will display the data in the txtMessageHistory control. As Windows controls are not thread-safe, you need to use a delegate,
delUpdateHistory(), to update the controls:
Public Sub ReceiveMessage(ByVal ar As IAsyncResult)
Try
Dim bytesRead As Integer
bytesRead = client.GetStream.EndRead(ar)
If bytesRead < 1 Then
Exit Sub
Else
Dim para() As Object = _
{System.Text.Encoding.ASCII.GetString( _
data, 0, bytesRead)}
Me.Invoke(New delUpdateHistory( _
AddressOf Me.UpdateHistory), para)
End If
client.GetStream.BeginRead( _
data, 0, CInt(client.ReceiveBufferSize), _
AddressOf ReceiveMessage, Nothing)
Catch ex As Exception
End Try
End Sub
The
delUpdateHistory() delegate is used to invoke the
UpdateHistory() function in the main thread:
'---delegate and subroutine to update the
' TextBox control
Public Delegate Sub delUpdateHistory( _
ByVal str As String)
Public Sub UpdateHistory(ByVal str As String)
txtMessageHistory.AppendText(str)
End Sub
Private Sub Form1_FormClosing( _
ByVal sender As Object, _
ByVal e As _
System.Windows.Forms.FormClosingEventArgs) _
Handles Me.FormClosing
Disconnect()
End Sub
Finally, the
Disconnect() subroutine disconnects the client from the server:
Public Sub Disconnect()
'---Disconnect from server
Try
client.GetStream.Close()
client.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Testing the Application
To test the applications, first run the server by pressing F5 in Visual Studio 2005. You want to launch multiple copies of the client to test the multi-user capabilities of the server. To do this, compile the client-side code files
provided with this article into an .exe file. Run multiple copies of Winclient.exe and sign in and chat at the same time.
In this article, I've shown how the TcpClient class allows you to perform asynchronous communication between two computers. While the chat application developed in this article is simple, it is a foundation on which to build more complicated chat applications. In my next article I'll do exactly that, adding FTP, private chats, and more to this simple chat application.