Applied C#.NET Socket Programming

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.

OSI Stack
                                                                  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.

client Server 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.

  1. IPAddress ipa = IPAddress.Parse("192.168.1.10");  
  2. 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.
  1. Console.WriteLine("IP address is=" + IPAddress.Loopback.ToString());  
  2. 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.
  1. using System;  
  2. using System.Net;  
  3. using System.Net.Sockets;  
  4.   
  5. namespace SocketPro  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void GetHostInfo(string host)  
  10.         {  
  11.             try  
  12.             {  
  13.                 // Attempt to resolve DNS for given host or address  
  14.                 IPHostEntry hostInfo = Dns.GetHostEntry(host);  
  15.   
  16.                 // Display host name  
  17.                 Console.WriteLine("\tHost Name: " + hostInfo.HostName);  
  18.   
  19.                 // Display list of IP addresses for this host  
  20.                 Console.Write("\tIP Addresses: ");  
  21.                 foreach (IPAddress ipaddr in hostInfo.AddressList)  
  22.                 {  
  23.                     Console.Write(ipaddr.ToString() + " ");  
  24.                 }  
  25.                 Console.WriteLine();  
  26.             }  
  27.             catch (Exception)  
  28.             {  
  29.                 Console.WriteLine("\tUnable to resolve host: " + host + "\n");  
  30.             }  
  31.         }  
  32.         static void Main(string[] args)  
  33.         {  
  34.             foreach (String arg in args)  
  35.             {  
  36.                 Console.WriteLine("\nTesting for="+arg);  
  37.                 GetHostInfo(arg);  
  38.             }  
  39.             Console.ReadKey();  
  40.         }  
  41.     }  
  42. }  
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.
  1. using System;  
  2. using System.Net;  
  3. using System.Net.Sockets;   
  4. using System.IO;  
  5. using System.Text;  
  6. using System.Windows.Forms;  
  7.   
  8. namespace TcpAction  
  9. {  
  10.     public partial class tcpSend : Form  
  11.     {  
  12.         public tcpSend()  
  13.         {  
  14.             InitializeComponent();  
  15.         }  
  16.   
  17.         private void btnSend_Click(object sender, EventArgs e)  
  18.         {  
  19.             string HostName = txtHost.Text;  
  20.             int prt = Int32.Parse(txtPort.Text);    
  21.   
  22.             TcpClient tc = new TcpClient(HostName,prt);  
  23.   
  24.             NetworkStream ns = tc.GetStream();  
  25.   
  26.             FileStream fs = File.Open(txtFile.Text, FileMode.Open);  
  27.   
  28.             int data = fs.ReadByte();  
  29.   
  30.             while (data != -1)  
  31.             {  
  32.                 ns.WriteByte((byte)data);  
  33.                 data = fs.ReadByte();   
  34.             }  
  35.             fs.Close();  
  36.             ns.Close();  
  37.             tc.Close();   
  38.         }  
  39.   
  40.         private void btnOpen_Click(object sender, EventArgs e)  
  41.         {  
  42.             OpenFileDialog ofd = new OpenFileDialog();  
  43.             ofd.ShowDialog();  
  44.             string str = ofd.FileName;  
  45.             txtFile.Text = str.ToString();  
  46.         }  
  47.     }  
  48. }  
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.
  1. using System;  
  2. using System.IO;  
  3. using System.Net;  
  4. using System.Net.Sockets;  
  5. using System.Threading;    
  6. using System.Windows.Forms;  
  7.   
  8. namespace tcpReceive  
  9. {  
  10.     public partial class Form1 : Form  
  11.     {  
  12.         public delegate void testDelegate(string str);  
  13.         public Form1()  
  14.         {  
  15.             InitializeComponent();  
  16.             Thread t = new Thread(new ThreadStart(ListenData));  
  17.             t.Start();   
  18.         }  
  19.   
  20.         public void test(string str)  
  21.         {  
  22.             txtData.Text = str;   
  23.         }  
  24.         public void ListenData()  
  25.         {  
  26.             IPAddress ipad = IPAddress.Parse("127.0.0.1");  
  27.             Int32 prt = 2112;  
  28.             TcpListener tl = new TcpListener(ipad, prt);  
  29.             tl.Start();  
  30.   
  31.             TcpClient tc = tl.AcceptTcpClient();  
  32.   
  33.             NetworkStream ns = tc.GetStream();  
  34.             StreamReader sr = new StreamReader(ns);  
  35.   
  36.             string result = sr.ReadToEnd();  
  37.             Invoke(new testDelegate(test), new object[] { result });  
  38.   
  39.             tc.Close();  
  40.             tl.Stop();  
  41.         }  
  42.     }  
  43. }  
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.

tcp send data
                                       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:

tcp recieve
                                                            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.

Socket roadmap
                                                    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.
  1. using System;  
  2. using System.Net;  
  3. using System.Net.Sockets;  
  4. using System.IO;  
  5. using System.Text;  
  6.   
  7. namespace TCPserver  
  8. {  
  9.     class Program  
  10.     {  
  11.         private const int BUFSIZE = 32;  
  12.   
  13.         static void Main(string[] args)  
  14.         {  
  15.             if (args.Length > 1) // Test for correct of args  
  16.                 throw new ArgumentException("Parameters: [<Port>]");  
  17.   
  18.             int servPort = (args.Length == 1) ? Int32.Parse(args[0]) : 7;  
  19.   
  20.             TcpListener listener = null;  
  21.   
  22.             try  
  23.             {  
  24.                 // Create a TCPListener to accept client connections  
  25.                 listener = new TcpListener(IPAddress.Any, servPort);  
  26.                 listener.Start();  
  27.             }  
  28.             catch (SocketException se)  
  29.             {  
  30.                 Console.WriteLine(se.Message);  
  31.                 Environment.Exit(se.ErrorCode);  
  32.             }  
  33.   
  34.             byte[] rcvBuffer = new byte[BUFSIZE]; // Receive buffer  
  35.             int bytesRcvd; // Received byte count  
  36.   
  37.             for (; ; )  
  38.             { // Run forever, accepting and servicing connections  
  39.   
  40.                 TcpClient client = null;  
  41.                 NetworkStream ns = null;  
  42.                 try  
  43.                 {  
  44.                     client = listener.AcceptTcpClient(); // Get client connection  
  45.                     ns = client.GetStream();  
  46.                     Console.Write("Handling client - ");  
  47.   
  48.                     // Receive until client closes connection  
  49.                     int totalBytesEchoed = 0;  
  50.                     while ((bytesRcvd = ns.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)  
  51.                     {  
  52.                         ns.Write(rcvBuffer, 0, bytesRcvd);  
  53.                         totalBytesEchoed += bytesRcvd;  
  54.                     }  
  55.                     Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);  
  56.   
  57.                     ns.Close();  
  58.                     client.Close();  
  59.   
  60.                 }  
  61.                 catch (Exception e)  
  62.                 {  
  63.                     Console.WriteLine(e.Message);  
  64.                     ns.Close();  
  65.                 }  
  66.             }  
  67.         }  
  68.     }  
  69. }  
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:

TCP server

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:
  1. Telnet 127.0.0.1 30  
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:

hendling client

                              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.
  1. using System;  
  2. using System.IO;  
  3. using System.Net;  
  4. using System.Net.Sockets;  
  5. using System.Text;  
  6.   
  7. namespace TCPclient  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Main(string[] args)  
  12.         {  
  13.             if ((args.Length < 2) || (args.Length > 3))  
  14.             {  
  15.                 throw new ArgumentException("Parameters: <Server> <Word> [<Port>]");  
  16.             }  
  17.   
  18.             String server = args[0]; // Server name or IP address  
  19.   
  20.             // Convert input String to bytes  
  21.             byte[] byteBuffer = Encoding.ASCII.GetBytes(args[1]);  
  22.   
  23.             // Use port argument if supplied, otherwise default to 7  
  24.             int servPort = (args.Length == 3) ? Int32.Parse(args[2]) : 7;  
  25.   
  26.             TcpClient client = null;  
  27.             NetworkStream ns = null;  
  28.   
  29.             try  
  30.             {  
  31.                 // Create socket that is connected to server on specified port  
  32.                 client = new TcpClient(server, servPort);  
  33.   
  34.                 Console.WriteLine("Connected to server......");  
  35.   
  36.                 ns = client.GetStream();  
  37.   
  38.                 // Send the encoded string to the server  
  39.                 ns.Write(byteBuffer, 0, byteBuffer.Length);  
  40.   
  41.                 Console.WriteLine("Sent {0} bytes to server...", byteBuffer.Length);  
  42.   
  43.                 int totalBytesRcvd = 0; // Total bytes received so far  
  44.                 int bytesRcvd = 0; // Bytes received in last read  
  45.   
  46.                 // Receive the same string back from the server  
  47.                 while (totalBytesRcvd < byteBuffer.Length)  
  48.                 {  
  49.                     if ((bytesRcvd = ns.Read(byteBuffer, totalBytesRcvd,  
  50.                     byteBuffer.Length - totalBytesRcvd)) == 0)  
  51.                     {  
  52.                         Console.WriteLine("Connection closed prematurely.");  
  53.                         break;  
  54.                     }  
  55.                     totalBytesRcvd += bytesRcvd;  
  56.                 }  
  57.                 Console.WriteLine("Received {0} bytes from server: {1}", totalBytesRcvd,  
  58.                  Encoding.ASCII.GetString(byteBuffer, 0, totalBytesRcvd));  
  59.   
  60.             }  
  61.             catch (Exception e)  
  62.             {  
  63.                 Console.WriteLine(e.Message);  
  64.             }  
  65.             finally  
  66.             {  
  67.                 ns.Close();  
  68.                 client.Close();  
  69.             }  
  70.   
  71.         }  
  72.     }  
  73. }  
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:

tcp client

                                                      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:

server in listening mode

                                 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:

socket testing
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.

Up Next
    Ebook Download
    View all
    Learn
    View all