Word file

advertisement
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 Berkeley sockets library. The Winsock library functions are wrapped in
the .NET Socket class.
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 Boulder, Colorado, that provides the exact time of day
(in Greenwich Mean Time) upon request. You can contact that server via
the Telnet utility (from a command line) to see what output it provides. The
13 on the command line is the port number at which the service is provided.
Notice that the command line provides a domain name and port number, just
what is needed to construct a remote endpoint.
c:>telnet time-A.timefreq.bldrdoc.gov 13
54058 06-11-19 17:02:34 00 0 0 630.4 UTC(NIST) *
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.
Download