This article will give you a broad understanding of key networking concepts, such as ISO stack and TCP/IP, and how applications can logically and physically distribute in a network environment. This article also covers .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 to manipulate IP address and DNS lookup. Finally, we’ll examine the anatomy of sockets in depth and see how to develop a client and server application in order to set up socket connection. Internet Networking Protocol A network protocol is a standard set of rules that determine how systems will communicate across networks. Two different systems that use the same protocols can communicate and understand each other despite their differences. The OSI reference model provides important guidelines used by vendors, engineers and others. The internet protocol facilitates the transmission and reception of data between participating client and server applications in a packet-switched network environment.
Packet switched networks mean that data travelling along network pathways is divided into small, routable packages referred to as a packet. If a communication link between two points on a network goes down, the packet can be routed through the remaining network connection to their intended destination. The internet protocols work together as a layered protocol stack which 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 Network layer, we need a 32 bit IP address (choose from A, B, C, D and E class). A datagram in the network layer needs 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, called a port number, to choose among multiple processes running on the destination host. In the internet paradigm, the port numbers are between 0 to 65535 and are chosen randomly by the transport layer software running on the client host. These are called ephemeral ports (range 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 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 use 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 Sockets TCP and UDP are transport protocols that applications use to get their data across a network. They both use ports to communicate with upper OSI layers and to keep track of various conversations that take place simultaneously. The ports are also the mechanism used to identify how other computers access services. When a TCP or UDP message is formed, source and destination ports are contained within the header information along with the source and destination IP addresses. This make up a socket.
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 top of which are the System.Net.Socket APIs. The library can be used for both responding and initiating new network communication, which makes them perfect for both 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 incoming and outgoing communication.
Bind: Servers must bind a socket to an address in order to establish a local name. A bind enables external communication to connect to and send messages through new end points, and also enables the socket to read messages off of it and send its own.
Listen: By executing optional listen operation, a socket notifies the OS that it is interested in participating in a message queue which consumes incoming requests for further processing.
Accept: A socket accepts an incoming request by leaving a new socket, which can be used to read and write to the client.
Connect: This is 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 has hooked-up 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 which releases all the resources held by the socket.
There are two types of sockets generally created: Server-side socket and Client-side socket. The server-side application, whose purpose is to process network requests, commonly sits inside a loop and rapidly consumes and dispatches such requests. The basic pattern is to create, bind, listen and accept until the program is shut down. For instance, web servers (IIS, apache) receive and process 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. The System.Net is generally concerned with higher-level operations, for example, downloading and uploading files, and making web requests using HTTP and other protocols, whereas System.Net.Sockets contain classes to perform lower-level operations. IPAddress The IP (internet protocol) address is a 32 bit or 128 bit number that uniquely identifies a particular computer on the network. In TCP/IP, it takes two pieces of information to identify a particular program: an Internet address, represented by an IP, and a port number, the additional address 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 which can take a long integer IP argument in its constructor, or process a string with the dotted-quad representation of an IP address using its Parse() method. IPAddress ipa = IPAddress.Parse(“192.168.1.10”); Console.WriteLine(“IP address is=”+ipa.ToString()); IPAddress class also provides a number of constant static fields to return special addresses such as the computer loopback address. Console.WriteLine(“IP address is=” + IPAddress.Loopback.ToString()); Console.WriteLine(“Broadcast address is=” + IPAddress.Broadcast.ToString()); DNS The DNS class is able to communicate with your default DNS server to obtain IP address. Here, the two static methods Resolve() and GetHostByAddress() are used to retrieve the details of the host by using the IPHostEntry class. IPHostEntry The IPHostEntry class encapsulates information related to a particular host computer. SocketPro.cs class demonstrates 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. [c] using System; using System.Net; using System.Net.Sockets; namespace SocketPro { class Program { static void GetHostInfo(string host) { try { // Attempt to resolve DNS for given host or address IPHostEntry hostInfo = Dns.GetHostEntry(host); // Display host name Console.WriteLine(“tHost Name: " + hostInfo.HostName); // Display list of IP addresses for this host 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(); } } } [/c] After compiling this program, open the application executable in the command prompt with arguments such as host name or IP address, then the output would be as follows:
TCP TCP (Transmission Control Protocol) classes are used to connect and send data between two sockets 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. TcpClient class enables creating and using a TCP connection while TcpListener enables listening for incoming TCP connection requests 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 how these classes work, you need to build two applications. In the first application, we set up a socket connection on a specific port in order to implement the file sending functionality and in the second application, we listen to that connection to receive the file. TcpSend Application Here in this demonstration, we set up a socket connection on port 2112 in order 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. [c] 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(); } } } [/c] TcpReceive Application On the other side of connection, the TcpReceive application displays 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 creat a StreamReader to convert the incoming network data into a string. [c] 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(); } } } [/c] After successfully compiling both files, first run the Tcp Receive application in order to put it into listen mode to receive the file. Then execute the Tcp Send Data application and there mention the host name as 127.0.0.1 (localhost), port (2112), upload a file, and finally hit the Send Data button.
When you upload a file and send it, the Tcp Receive application receives this file and prints its contents into a textbox as follows:
Socket in Depth It is useful to give a brief overview and history of sockets on Microsoft Windows before we begin describing the details of the .NET socket classes. The 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, which 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.
TCP Socket Server
The server’s job is to set up an 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 then calls the Start() method. This socket listens for incoming connections on the specified port then calls the AcceptTcpClient() method of TcpListener 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 AcceptTcp- Client() call. Thereafter, it communicates with the client using the Read() and Write() methods of TcpClient’s NetworkStream and finally closes the new client socket connection and stream using the Close() methods of NetworkStream and TcpClient.
[c] 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) // Test for correct of args throw new ArgumentException(“Parameters: []”);
int servPort = (args.Length == 1) ? Int32.Parse(args[0]) : 7;
TcpListener listener = null;
try { // Create a TCPListener to accept client connections listener = new TcpListener(IPAddress.Any, servPort); listener.Start(); } catch (SocketException se) { Console.WriteLine(se.Message); Environment.Exit(se.ErrorCode); }
byte[] rcvBuffer = new byte[BUFSIZE]; // Receive buffer int bytesRcvd; // Received byte count
for (; ; ) { // Run forever, accepting and servicing connections
TcpClient client = null; NetworkStream ns = null; try { client = listener.AcceptTcpClient(); // Get client connection ns = client.GetStream(); Console.Write(“Handling client – “);
// Receive until client closes connection 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(); } } } } } [/c]
Now, compile this application and open 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.
To test if the standard echo server is running, try telnetting to any port such as 30 on the server: Telnet 127.0.0.1 30 Enter some data in 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:
TCP Client Socket A TCP client initiates communication with a server that is passively waiting to be contacted. The typical TCP client goes through three steps: First it constructs an instance of TcpClient. A TCP connection can be created implicitly in the constructor by specifying the remote host and port, or explicitly by using the Connect() method. Second, it communicates 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, it closes the connection by calling the Close() method of TcpClient [c] 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: []”); } String server = args[0]; // Server name or IP address // Convert input String to bytes byte[] byteBuffer = Encoding.ASCII.GetBytes(args[1]); // Use port argument if supplied, otherwise default to 7 int servPort = (args.Length == 3) ? Int32.Parse(args[2]) : 7; TcpClient client = null; NetworkStream ns = null; try { // Create socket that is connected to server on specified port client = new TcpClient(server, servPort); Console.WriteLine(“Connected to server……”); ns = client.GetStream(); // Send the encoded string to the server ns.Write(byteBuffer, 0, byteBuffer.Length); Console.WriteLine(“Sent {0} bytes to server…”, byteBuffer.Length); int totalBytesRcvd = 0; // Total bytes received so far int bytesRcvd = 0; // Bytes received in last read // Receive the same string back from the server 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(); } } } } [/c] 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:
It is important to put the server in listening mode first before executing the client. Once the data is sent from client side, the total number entered bytes are shown immediately:
You can test the established socket on port 20 via Windows netstat command to ensure client server socket connectivity.
Synopsis In this article, we have come across socket programming by using TcpListener and TcpClient classes to create client-server applications. We learned how to handle the details of establishing a network connection between client and server application, and how to send data between server and client so they can perform useful work. You have also seen some of the .NET base classes of System.Net namespace that deals with opening client connections on the network.