Abstract
This article explains the key networking concepts, for instance ISO stack, of TCP/IP under the C# framework by employing its essential socket classes and how applications can logically and physically be distributed in a network environment. This paper rovides a close encounter with the .NET base classes for using various network protocols, particularly HTTP, TCP and UDP, to access networks and the internet as a client. Apart from that, we shall learn how to manipulate an IP address and do a DNS lookup to establish a socket connection between the client and server. Finally, we'll examine the anatomy of sockets in depth and examine how to develop a client and server application to set up a socket connection.
Internet Networking Protocol
A networking protocol is a standard set of rules that determine how systems will communicates across networks. Two systems that use the same protocols can communicate and understand each other despite their differences. The OSI reference model provides important guidelines to vendors, engineers and others for network communication protocol development. The internet protocol facilitates the transmission and reception of data between participating client and server applications in a packet-switched network environment.
Figure 1.1 OSI Layers
Packet switched networks means data travelling along network pathways is divided into small, routable packages referred to as packet. If a communication link between two points on a network goes down, the packet can be routed through a remaining network connection to their intended destination. The internet protocols work together as a layered protocol stack that consists of the application, presentation, network, transport, Data Link Layer and physical layers. Each layer in the protocol stack provides a set of services to the layer above it.
The transport layer is responsible for process-to-process delivery. The delivery of a packet, part of a message, from one process to another. Two processes communicate in a client/server relationship paradigm. A process on the local host called a client, needs services from a process usually on the remote host, called a server.
Addressing
Whenever we need to deliver something to one specific destination among many, we need an address. At the Data Link Layer, we need a MAC address whereas at the Network Layer, we need a 32 bit IP address (choose from A, B, C, D and E class). A datagram in the Network Layer requires a destination IP address for delivery and a source IP address for the destination reply.
At the Transport layer, we need a transport layer address, which is called a port number, to choose among multiple processes running on the destination host. In the internet paradigm, the port numbers are between 0 and 65535 and are chosen randomly by the transport layer software running on the client host. These are called ephemeral ports (range from 1024 to 49151). The server process must also define itself with a port number. These ports, however, cannot be chosen randomly. If the computer at the server site runs a server process and assigns a random number as the port number, the process at the client site that wants to access that server and uses its services will not know the port number. So, the internet has decided to use universal port numbers for servers, called well-known port numbers (range 0 to 1023).
Anatomy of a Socket
TCP and UDP are transport protocols that applications utilize to transport their data across a network. They both use ports to communicate with upper OSI layers and to keep track of various conversations that occur simultaneously. The ports have the mechanism to identify how other computers access the services. When a TCP or UDP message is formed, a source and destination port is contained within the header information along with the source and destination IP address. This makes a socket.
Figure 1.2 Socket Demonstration
A socket represents one end of a connection between two end points on the network, much like streams. Windows ships with an implementation of sockets called WinSock32, on the top of which are the System.Net.Socket APIs. The library can be used for both responding to an existing network communication and initiating a new network communication, that makes them perfect for client-server applications. They are often used for work-intensive server applications, such as web servers. The socket implementations in the System.Net.Socket namespace can be described by the following components:
- Socket: This operation is used to create a new socket end point, allocating operating system resources that can then be used to execute an incoming and outgoing communication.
- Bind: Servers must bind a socket to an address to establish a local name. A bind enables external communication to connect to and send messages through new end points, enable the socket to read messages off of it and send its own.
- Listen: The socket shows its interest in message queues participation by notifying the OS by executing an optional listen operation, where message queues consume incoming requests for further processing.
- Accept: A socket accepts an incoming request by leaving a new socket, that can be used to read and write to the client.
- Connect: Used to connect to a server-side network end point. Once the target end point accepts the request, the socket can be used to read from and write data to the socket.
- Send: once a socket is connected to an end point, sending data initiates a message that the other point is then able to receive.
- Receive: after the end point to which a socket is connected sends data, a socket may consume that data by receiving it.
- Close: when a connection has finished its session, a socket must be closed that releases all the resources held by the socket.
There are two types of a sockets generally created, server-side sockets and client-side sockets. The server-side application whose purpose is to process network request commonly sits inside a loop and rapidly consumes and dispatches the requests. The basic pattern is to create, bind, listen to and accept until the program is shut down. For instance, a web server (such as IIS or apache) that receives and processes incoming requests. The client-side socket communicates with existing end points on the network. The only operation required is connecting to server, aside from sending and receiving data.
Utility Networking Classes
The two namespaces of most interest for networking are System.Net and System.Net.Sockets. System.Net is generally concerned with higher-level operations, for example, downloading and uploading a file and making web requests using HTTP and other protocols, whereas System.Net.Sockets contains classes to do lower-level operations.
IPAddress Class
An Internet Protocol (IP) address is a 32 bit or 128 bit number that uniquely identifies a specific computer on the network. TCP/IP uses two pieces of information to identify a specific program: an Internet address, used by IP and a port number, the additional address is interpreted by the transport protocol (TCP or UDP). The IPAddress class represents an IP address. The .NET encapsulates the IP addresses abstraction in the IPAddress class that can take a long integer IP argument in its constructor, or process a string with the dotted-quad representation of an IP address by using its Parse() method.
- IPAddress ipa = IPAddress.Parse("192.168.1.10");
- Console.WriteLine("IP address is="+ipa.ToString());
The
IPAddress class also provides a number of constant static fields to return a special address, such as the computer loopback address.
- Console.WriteLine("IP address is=" + IPAddress.Loopback.ToString());
- Console.WriteLine("Broadcast address is=" + IPAddress.Broadcast.ToString());
Dns Class
The
Dns class is able to communicate with your default DNS server to obtain an IP address. Here, the two static methods
Resolve() and
GetHostByAddress() are used to retrieve the details of the host using the
IPHostEntry class.
IPHostEntry Class
The
IPHostEntry class encapsulates the information related to a specific host computer. Here, the SocketPro class shows the use of the
IPAddress,
IPHostEntry and
Dns classes. The program takes a list of names or IP addresses as command-line parameters and prints the name and an IP address of the local host, followed by the names and IP addresses of the hosts specified on the command line.
- using System;
- using System.Net;
- using System.Net.Sockets;
-
- namespace SocketPro
- {
- class Program
- {
- static void GetHostInfo(string host)
- {
- try
- {
-
- IPHostEntry hostInfo = Dns.GetHostEntry(host);
-
-
- Console.WriteLine("\tHost Name: " + hostInfo.HostName);
-
-
- Console.Write("\tIP Addresses: ");
- foreach (IPAddress ipaddr in hostInfo.AddressList)
- {
- Console.Write(ipaddr.ToString() + " ");
- }
- Console.WriteLine();
- }
- catch (Exception)
- {
- Console.WriteLine("\tUnable to resolve host: " + host + "\n");
- }
- }
- static void Main(string[] args)
- {
- foreach (String arg in args)
- {
- Console.WriteLine("\nTesting for="+arg);
- GetHostInfo(arg);
- }
- Console.ReadKey();
- }
- }
- }
After compiling this program, open the application executable over the command prompt with essential arguments such as host name or IP address, then the output would be as follows.
Transmission Control Protocol (TCP)The Transmission Control Protocol (TCP) classes are used to connect and send data between two socket endpoints (IP address + port). TCP offers
TcpClient and
TcpListener classes to establish a socket connection on well-known ports such as 80, 443 and 21. The
TcpClient class enables creation and of use a TCP connection while
TcpListener enables listening for an incoming TCP connection request with its
Start() method. When a connection request arrives, you can use the
AcceptSocket() method to return a socket for communication with the remote machine.
To demonstrate the operation of these classes, you need to build two applications. In the first application, we 'll set up a socket connection on a specific port to implement the file sending functionality and in the second application, we'll listen for that connection to receive the file.
TcpSend Application
Here, in the following demonstration, we are setting up a socket connection on port 2112 to send an uploaded file to the server from the localhost. This example creates the TcpClient using a host name and a port number. After retrieving an instance of the NetworkStream class, you open the source code file and begin to read bytes.
- using System;
- using System.Net;
- using System.Net.Sockets;
- using System.IO;
- using System.Text;
- using System.Windows.Forms;
-
- namespace TcpAction
- {
- public partial class tcpSend : Form
- {
- public tcpSend()
- {
- InitializeComponent();
- }
-
- private void btnSend_Click(object sender, EventArgs e)
- {
- string HostName = txtHost.Text;
- int prt = Int32.Parse(txtPort.Text);
-
- TcpClient tc = new TcpClient(HostName,prt);
-
- NetworkStream ns = tc.GetStream();
-
- FileStream fs = File.Open(txtFile.Text, FileMode.Open);
-
- int data = fs.ReadByte();
-
- while (data != -1)
- {
- ns.WriteByte((byte)data);
- data = fs.ReadByte();
- }
- fs.Close();
- ns.Close();
- tc.Close();
- }
-
- private void btnOpen_Click(object sender, EventArgs e)
- {
- OpenFileDialog ofd = new OpenFileDialog();
- ofd.ShowDialog();
- string str = ofd.FileName;
- txtFile.Text = str.ToString();
- }
- }
- }
TcpReceive Application
On the other side of the connection, the
Tcp Receive application showcases the received file after the transmission is finished. Here, you use the
TcpClient object returned by
AcceptTcpClient() to open a new stream for reading. We are creating a
StreamReader class to convert the incoming network data into a string.
- using System;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using System.Windows.Forms;
-
- namespace tcpReceive
- {
- public partial class Form1 : Form
- {
- public delegate void testDelegate(string str);
- public Form1()
- {
- InitializeComponent();
- Thread t = new Thread(new ThreadStart(ListenData));
- t.Start();
- }
-
- public void test(string str)
- {
- txtData.Text = str;
- }
- public void ListenData()
- {
- IPAddress ipad = IPAddress.Parse("127.0.0.1");
- Int32 prt = 2112;
- TcpListener tl = new TcpListener(ipad, prt);
- tl.Start();
-
- TcpClient tc = tl.AcceptTcpClient();
-
- NetworkStream ns = tc.GetStream();
- StreamReader sr = new StreamReader(ns);
-
- string result = sr.ReadToEnd();
- Invoke(new testDelegate(test), new object[] { result });
-
- tc.Close();
- tl.Stop();
- }
- }
- }
After successfully compiling both files, first run the
Tcp Receive application to put it into listen mode to receive the file, then execute the Tcp
Send Data application and later mention there the host name as 127.0.0.1 (localhost), port (2112) and upload a file, finally hitting the
Send Data button.
Figure 1.3 Data Sending
When we upload a file and send it across the network, the
Tcp Receive application receives this file and displays its contents into a TextBox as follows:
Figure 1.4 Data Receiving
Socket in Depth
It is essential to describe a brief overview and history of sockets on Microsoft Windows before we begin describing the details of the .NET socket classes. A socket was initially created for the Berkeley Software Distribution (BSD) of UNIX. A version of sockets for Microsoft Windows called WinSock 1.1 was initially released in 1992 and is currently on version 2.0; In order to allow access to the underlying sockets interface, Microsoft implemented a .NET Socket class, that is a wrapper around the WinSock socket functions and has most of the versatility (and complexity) of sockets interface exposed. Then three higher-level socket classes,
TcpClient,
TcpListener and
UdpClient, were implemented by using the .NET Socket wrapper class.
Figure 1.5 .NET Socket Classes
TCP Socket Server The server's job is to set up endpoint for clients to connect to and passively wait for connections. The typical TCP server first constructs a
TcpListener instance, specifying the local address and port and calls the Start() method. This socket listens for incoming connections on the specified port then calls the
AcceptTcpClient() method of the
TcpListener class to get the next incoming client connection. Upon establishment of a new client connection, an instance of
TcpClient for the new connection is created and returned by the
AcceptTcpClient() call. Thereafter, communication with the client is done using the Read() and Write() methods of the
NetworkStream class and finally the client socket connection and stream are closed using the Close() methods of
NetworkStream and
TcpClient.
- using System;
- using System.Net;
- using System.Net.Sockets;
- using System.IO;
- using System.Text;
-
- namespace TCPserver
- {
- class Program
- {
- private const int BUFSIZE = 32;
-
- static void Main(string[] args)
- {
- if (args.Length > 1)
- throw new ArgumentException("Parameters: [<Port>]");
-
- int servPort = (args.Length == 1) ? Int32.Parse(args[0]) : 7;
-
- TcpListener listener = null;
-
- try
- {
-
- listener = new TcpListener(IPAddress.Any, servPort);
- listener.Start();
- }
- catch (SocketException se)
- {
- Console.WriteLine(se.Message);
- Environment.Exit(se.ErrorCode);
- }
-
- byte[] rcvBuffer = new byte[BUFSIZE];
- int bytesRcvd;
-
- for (; ; )
- {
-
- TcpClient client = null;
- NetworkStream ns = null;
- try
- {
- client = listener.AcceptTcpClient();
- ns = client.GetStream();
- Console.Write("Handling client - ");
-
-
- int totalBytesEchoed = 0;
- while ((bytesRcvd = ns.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
- {
- ns.Write(rcvBuffer, 0, bytesRcvd);
- totalBytesEchoed += bytesRcvd;
- }
- Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);
-
- ns.Close();
- client.Close();
-
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- ns.Close();
- }
- }
- }
- }
- }
Now, compile this application and open a command prompt. Here specify the application name with a port such as 20 (if you don't specify any port then the default is 7) on which you want to establish communication as in the following:
To test whether the standard echo server is running or not, telnet to that port such as 30 on the server as in the following:
Enter some data at the Telnet terminal and exit. If the server establishes a socket connection on port 30 properly then the server will respond by showing the entered character bytes as follows:
Figure 1.6 Server in listening mode
TCP Client Socket
A TCP client initiates communication with a server that is passively waiting to be contacted. In TCP the client typicallygoes through the three steps. First construct an instance of
TcpClient. A TCP connection can be created implicitly in the constructor by specifying the remote host and port, or explicitly using the Connect() method. Second, communicate using the socket's stream. A connected instance of
TcpClient contains a
NetworkStream that can be used like any other .NET I/O stream. Finally close the connection by calling the Close() method of
TcpClient.
- using System;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
-
- namespace TCPclient
- {
- class Program
- {
- static void Main(string[] args)
- {
- if ((args.Length < 2) || (args.Length > 3))
- {
- throw new ArgumentException("Parameters: <Server> <Word> [<Port>]");
- }
-
- String server = args[0];
-
-
- byte[] byteBuffer = Encoding.ASCII.GetBytes(args[1]);
-
-
- int servPort = (args.Length == 3) ? Int32.Parse(args[2]) : 7;
-
- TcpClient client = null;
- NetworkStream ns = null;
-
- try
- {
-
- client = new TcpClient(server, servPort);
-
- Console.WriteLine("Connected to server......");
-
- ns = client.GetStream();
-
-
- ns.Write(byteBuffer, 0, byteBuffer.Length);
-
- Console.WriteLine("Sent {0} bytes to server...", byteBuffer.Length);
-
- int totalBytesRcvd = 0;
- int bytesRcvd = 0;
-
-
- while (totalBytesRcvd < byteBuffer.Length)
- {
- if ((bytesRcvd = ns.Read(byteBuffer, totalBytesRcvd,
- byteBuffer.Length - totalBytesRcvd)) == 0)
- {
- Console.WriteLine("Connection closed prematurely.");
- break;
- }
- totalBytesRcvd += bytesRcvd;
- }
- Console.WriteLine("Received {0} bytes from server: {1}", totalBytesRcvd,
- Encoding.ASCII.GetString(byteBuffer, 0, totalBytesRcvd));
-
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
- finally
- {
- ns.Close();
- client.Close();
- }
-
- }
- }
- }
Finally, open the TCPclient.exe in the command prompt and send some string data via port 20 to server 127.0.0.1. It will echo back the output as follows:
Figure 1.7 Connecting Sockets
At first, it is mandatory to keep the server in listening mode before executing the client. Once the data begins to come from the client side, the total number of entered bytes yields immediately as followis:
Figure 1.8 Server in listening mode
You can test that the socket was establish on port 20 via the Windows netstat command to ensure the client server socket connectivity as follows:
Figure 1.9 socket testing
Synopsis
This article has described socket programming using
TcpListener and
TcpClient classes to create client-server applications. Here, we have learned how to handle the details of establishing a network connection between client and server applications, moreover how to send data between the server and client so they can do useful operations. You have also encountered the essential .NET base classes of the
System.Net namespace that deals with opening the client connection on the network.