An article the creates a chat client and server using Sockets to communicate using TCP/IP. One of the features of this example is it use of delegates to produce asynchronous connections and communications.

Introduction

This article shows how to create both ends of a TCP/IP socket connection
between two or more applications. These application may be run, on the same
machine, machines connected by a local area network or even machines
communicating across the internet*. A key feature of this method
is it does not your threads, instead using sockets in a non-blocking mode. In this example a server is
created that listens for clients to connect. Once a client connects it is added
to a list of active clients. If a client sends in a message it is broadcast to
all connected clients in much the same way as a chat group would operate.
Remoting would be a better way to do this, but we are here to learn about
Sockets.

* Note, to communicate across the internet will require the server
to have an IP address on the internet not hidden behind a proxy server as many
ISPs do.

Sequence of events

Before a client can connect, the server must be listening. The following
diagram shows the sequence of events that make up an asynchronous socket
session.

Running the sample

The sample code is divided into two application ChatServer that the clients
connect to and ChatClient that connects to the client. Build the ChatServer
first and test it using Telnet with the following command.

A message should appear on ChatServer indicating the address and the port
number the client can connected on. Anything typed into the telnet window should
echo back to all telnet windows connected to the server. Try making several
concurrent connections from various machines. Do not use localhost or the
127.0.0.1 address as the server application is only listening on the address
shown in the server start-up message.

Next run the ChatClient sample any try the same test using multiple instances
of ChatClient and Telnet across various machines.

Why use sockets with .NET?

.NET uses sockets in may instances such as WebServices and Remoting, but in
these instances the low level socket stuff is done for you and there is no
need to use sockets directly. However, when interfacing to other non .NET
systems sockets are a necessary and simple communication method. They can
be used to communicate with DOS, Windows and UNIX systems. The low level
sockets also allows you to avoid having to worry about registration,
privileges, domains, user id's, password and other troublesome security issues.

ChatServer / Listener

The server listens for clients to connect when a connection is requested the
server will accept the connection and return a welcome message. In the sample the connections are added
to an array of active clients m_aryClients. As client connect and
disconnect, this list will grow and shrink. It is not always possible to detect
the loss of a connection, so in a production system there should be some form of
polling to detect if the connection is still alive. When data is received on a
listener it is broadcast to all connected clients.

Two methods of listening are discussed below one using polling the other
events to detect connection requests.

Method 1 - Using polled TcpListener

Using the TcpListener class from System.Net.Sockets provides a
simple method to listen for client connections and process them. The following
code listens for a connection, accepts it and sends a welcome message with a
time stamp. If another connection is requested the old one is lost. Note, the
welcome message is returned in ASCII not UNICODE.

With the address identified we need to bind the listener to this address. Here
we are listening on port 399. It is good practice to read the port number
from from the Services file located in
"C:\WinNT\System32\drivers\etc\Services". The following code binds the
listener and begins to listen. An event handler is added pointing all
connection requests to OnConnectRequest. The application can now go about its
business without having to wait or poll for clients to connect.

When a client requests a connection, the connection request event handler is
fired as follows. The following code creates a client sends a welcome
message and and re-establishes the accept event handler.

This is expanded on in the sample code to allow the client socket to be kept in
a list and monitored for received data and disconnections. Disconnections are
detected on the client socket in the AsyncCallback event handler. The
ChatClient details this mechanism below.

ChatClient

The ChatClient is a windows form application that connects to the server and
displays messages that it receives and allow messages to be sent.

Connecting

The client connects connect to the server when the Connect button is pressed
with the following code;

If the connection already exists it is destroyed. A Socket is then created
and an end point established. The commented out code allows for the
simpler blocking connection attempt. BeginConnect is used to commence a non
blocking connection attempt. Note, even if a non-blocking connection is
attempted, the connection will block until the machine name is resolved into an
IP address, for this reason it is better to use the IP address than the machine
name if possible to avoid blocking. The following method is called
once the connection attempt is complete, it displays connection error or sets
up the receive data callback if connected OK.

When the above event is fired the receive data is assumed to be ASCII. The new
data is sent to the display by invoking a delegate. Although it is possible to
call Add() on the list to display the new data, it is a very bad idea because
the received data will most likely be running in another thread. Note the receive
callback must also be established again to continue to
receive more events. Even if more data was received than can be placed in the
input buffer, restabilising the receive callback will cause it to trigger until
all data has been read.

The AddMessage delegate is created to decouple socket thread
from user interface thread as follows;

Working in UNICODE

When data is received or is to be sent the data is held in an array of 8 bit
bytes. This data must be encoded to a format suitable for .NET when received
and a format suitable for the receiving application when sent. C# uses a
multibyte character encoding internally so data must be converted to that when
it is received and before it is sent out using the Encoding.ASCII or
Encoding.UNICODE static methods as necessary.

Don't believe a packet sent is a packet received

When the receive data event is triggered the received data is held in the input
buffer. During development a packet sent often corresponds to a single
trigging of the receive event and one complete set of data in the receive
buffer. This is definitely not the case in a production system. Data is not
packet-ised and actually consists of a stream of individual bytes that may be
broken up into may packets. Do not relay on receiving complete packets and
develop your own tags to indicate start and end of packet.

Conclusion

Although fairly simple to use Sockets do require a fair amount of code to get
working well. Where possible you should try to use WebServices or Remoting in
there place. Professional ADO.NET Programming by Wrox is a good book on other
stuff, check it out.

Hi, i`m working on a form that brings a DataSet from another computer, over internet. The idea of the communication of your chat got my atenttion. The only problem I have using your example is: When I test it with my 2 computers conected to my hub works fine, then I connect one with a dial up account and try again, the client (DSL) reaches server (dial-up) but I think that when server sends response to client conection is lost. My DSL Modem has an IP address, and my pc is assigned with other, could this be the problem? How would you make your app work in this conditions? Thanks in advanced