Network Programming in .NET
Basic concepts of network programming
Recall that in the lecture on
web services, there was a mention of the word “endpoint”. The WSDL document describes networks as
“collections of communication endpoints capable of exchanging
messages.”
For purposes of this lecture,
an “endpoint” is specified by an IP address and a port number. In this course, we won’t discuss
communication protocols at a lower level than that.
To set up a communication
channel between two computers, we will need a pair of endpoints, and a
communication protocol (some way of interpreting the data, and a way of
establishing the connection between the two endpoints, and a way of ending the
“conversation” politely). One such
communication protocol is called TCP (Transport
Control Protocol). We leave it to
your networking course to supply details about TCP and TCP/IP, but .NET makes
it possible to write network programs without knowing anything about TCP. There exist other communication protocols, but
we will use only TCP in this lecture.
To summarize: to create a usable communications channel we
need two endpoints and a protocol, or otherwise described, we need
·
A local IP
address
·
A local port
number
·
A remote IP
address
·
A remote port
number
·
A transport
protocol
A data structure that
encapsulates these five things is called a “socket”.
The concept of “socket” is
independent of .NET and even independent of Windows—every modern operating
system has a way to construct sockets.
Sockets are provided for
Windows in the Winsock library, and
for Unix in the
The Socket class also provides a Connect
method. That in turn calls on ancient
code in the Winsock library to
establish a connection between the local endpoint and the remote endpoint. That, of course, is only going to work if
the remote endpoint is “listening”,
i.e. is
ready to receive a connection request and do its part to establish the connection.
Once we have successfully established a
connection to a remote endpoint, we can use the Socket class method Receive
to receive data.
Domain Name Servers (DNS)
Where will we get the remote
IP address that we need? Well, we could
make a telephone call or send an email to the person at the other end—but that
isn’t a practical method for many purposes.
Instead we want to be able to mention an internet domain name. When you set up a computer to connect to the
internet, you have to specify one or more IP addresses for a domain name
server. Then your local operating system
knows how to request an IP address for a given domain name, and the DNS server
supplies it. Taking advantage of this
low-level service, we will be able to construct a remote endpoint from a domain
name and a port number.
First network programming example
Credit: I’ve imitated the first networking example in
Dr. Horstmann’s Java book Core Java 2. Here we do the
same example in .NET.
There is a server in
c:>telnet
time-A.timefreq.bldrdoc.gov 13
54058
Connection to host lost.
What is not quite obvious
from this printout (until I point it out) is that the output from the server
begins and ends with a newline. The
output specifies today’s date and time.
It doesn’t matter for our purposes what the rest of the output is. NIST is the National Institute of Standards, which maintains
this server.
What we want to do is write
our own low-level network program to establish a socket connection to the
NIST. Here are the steps to do this:
1. Make a new C# Windows Application called NetworkDemo. Drag onto your form a
button (label it Get Time) and a multi-line
list box to receive the output. The
idea is that when you click the button, a connection will be established to
the NIST server and the output will be displayed in the list box.
2. All the interesting code will be in the GetTimeButton_Click
handler. Here it is:
IPHostEntry resolvedServer;
IPEndPoint serverEndPoint;
Socket tcpSocket = null;
try
{
resolvedServer =
Dns.GetHostEntry("time-A.timefreq.bldrdoc.gov");
// here is where we use DNS to get an IP
adress
// Actually, an IPHostEntry can contain a whole
// list of possible IP addresses for the
domain.
foreach (IPAddress addr in
resolvedServer.AddressList)
{
tcpSocket = new
Socket(
addr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
serverEndPoint = new
IPEndPoint(addr, 13);
/* the time of
day service is on port 13 */
try
{
tcpSocket.Connect(serverEndPoint);
}
catch
{ // connect
failed so try the next one
if
(tcpSocket != null)
tcpSocket.Close();
continue;
}
break; // successful
connection
}
}
catch (SocketException err)
{
textBox1.Text = "Client
connection failed" + err.Message;
}
// now tcpSocket is open, use it
byte[] receiveBuffer = new byte[1024];
int nReceived;
String sReceived;
try
{
textBox1.Clear();
textBox1.Text = "";
nReceived = tcpSocket.Receive(receiveBuffer);
sReceived =
Encoding.UTF8.GetString(receiveBuffer).Substring(1,49);
// we don’t want the initial and final
newlines
textBox1.Text = sReceived.Trim();
// for some reason Trim doesn’t get rid of
the final
// newline character as the documentation
says it should.
/* The following
low-level code also works:
for (int i = 0; i
< nReceived; i++)
if(receiveBuffer[i] != '\n')
textBox1.Text
+= (char) receiveBuffer[i];
if(receiveBuffer[i] == '\0')
break;
*/
}
catch (SocketException)
{
textBox1.Text = "Some
unforeseen error occurred";
}
}
Server Sockets and Listeners
We were able to contact the
NIST server because it was already listening.
If we want to be able to set up a network connection entirely on our
own, say between two students in this class, then both computers will have to
create sockets, and one will have to be “listening”. If this is done at a low level, using methods of the Socket class, it requires a basic knowledge of threads. Here is the reason, which can be explained in
language-independent terms. What needs
to happen is this:
·
We need to create
a “server socket” and set it to “listen”.
It then waits until some client tries to connect.
·
Then, the socket needs to
“accept” the client, and at that point
the socket can be used to transmit and receive data.
While the socket is
“listening”, however,
execution doesn’t continue.
It “hangs” at the Listen line
of code and doesn’t go beyond until a connection is made. So, unless we want our whole program to be
unusable after you press the Listen
button until someone connects, we have
to create a separate “thread” of execution to handle the Listen command.
Since this is a common task
in network programming,
.NET has provided a special class to handle this task, that is
usable without a knowledge of the System.Threading library.
Specifically, there is a .NET class TcpListener. We will use that class in the next example.