2

 

Internet Classes

 

Before weaving our way into the Internet, let us investigate a few more fundamentals about the C# programming language, in order to acquire a superior appreciation of internet programming.

 

No computer in the world is equipped to recognize the letters of the alphabet on its own. Therefore, an internal representation has to be used. One such representation is called ASCII, which allots a number falling between 0 to 255, to each of the letters. Here, each letter is represented by a byte, which is an 8-bit number. For instance, the letter 'A' is represented by the number 65, while the letter 'a' is represented by the number 97.

 

However, languages such as Japanese and Chinese are remarkably visual, and require a larger range of numbers to represent them in totality. In order to accommodate the requirement of these languages, the computer industry came out with a standard called UNICODE, which can represent every known language in the world in its entirety. But, to accomplish this, this standard imposes the condition that every character in the language should be represented by a 16-bit number, and not by an 8-bit number.

 

 

 

a.cs

public class zzz

{

public static void Main()

{

byte i = 65;

System.Console.WriteLine(i);

System.Console.WriteLine((char)i);

i = (byte)'A';

System.Console.WriteLine(i);

}

}

 

Output

65

A

65

 

The C# language takes cognizance of the fact that the byte data type represents a number ranging from 0 to 255. Thus, by default, the WriteLine function displays the number that the byte represents. If we typecast the byte i to a char, the WriteLine function displays its ASCII equivalent instead. The C# language had to indoctrinate a new data type called 'char', which could represent a UNICODE character or a number ranging from 0 to 65535, since a byte is too diminutive to be able to accommodate this range of values. A literal or char 'A' cannot be initialized to a byte, since we cannot equate a smaller 8-bit data type to a larger 16-bit data type. Therefore, a cast operator has to be used.

 

a.cs

public class zzz

{

public static void Main()

{

char i = 'A';

System.Console.WriteLine(i);

}

}

 

 

Output

A

 

A variable of char data type cannot be equated to a number. It can only be initialized to a literal. The WriteLine function prefers to receive the 'char' data type, since it can display the value as a letter of the alphabet. Thus, the 'char' data type is used to store 16 bit UNICODE characters, while the 'byte' data type is employed to store small numbers. The 'string' data type is used for a sequence of UNICODE characters. 

 

a.cs

public class zzz

{

public static void Main()

{

byte [] i = new byte[5];

i[0] = 65; i[1] = 66;

System.Console.WriteLine(i);

char [] j = new char[5];

j[0] = 'A'; j[1] = 'B';

System.Console.WriteLine(j);

}

}

 

Output

System.Byte[]

AB

 

Dealing with an array of bytes is not as straightforward and uncomplicated as dealing with an array of chars. In case of an array of bytes, the WriteLine function merely displays the data type of the array, whereas, in the case of an array of chars, it displays the entire array. Thus, we need a technique, by means of which, we are able to display the bytes present in the form of an array of bytes, using the WriteLine function.

 

a.cs

public class zzz

{

public static void Main()

{

byte [] i = new byte[5];

i[0] = 65; i[1] = 66; i[2] = 67;i[3] = 68;

string s = System.Text.Encoding.ASCII.GetString(i, 1, 2);

System.Console.WriteLine(s);

}

}

 

Output

BC

 

The namespace System.Text.Encoding has a class called ASCII, which contains a static function GetString. This function accepts three parameters:

   The array of bytes, which is to be converted into a string.

   The starting point in the array ( in our case, 1) refers to the second element in the Array, which is the letter 'B'.

   The number of characters or the length of the string. We have specified it as 2. Hence, only 2 characters are seen.

 

The function returns a string, which is displayed by employing the WriteLine function. 

 

This methodology thereby, assists in converting a certain number of bytes from a byte array into a string, which can then be effortlessly printed by the WriteLine function.

 

The rationale for introducing this function in this chapter is due to the existence of a large number of functions that return data as an array of bytes. The approach demonstrated above may be employed, to display such data in all such cases.

 

a.cs

public class zzz

{

public static void Main()

{

string s1 = "vmukhi";

byte[] i;

char [] j = s1.ToCharArray();

i = System.Text.Encoding.ASCII.GetBytes(j);

string s = System.Text.Encoding.ASCII.GetString(i, 0, 5);

System.Console.WriteLine(s);

}

}

 

Output

vmukh

 

We now need to resolve another quandary. In the internet-related classes, functions read and write data in the form of an array of bytes.

 

We are comfortable dealing with strings, as they are the most natural data type available for representing data. However, this data type has to be converted into an array of bytes, if it has to be transmitted. But, there is no direct mechanism of attaining this conversion. The only possibility is to write our own function.

 

The string is first converted into an array of chars, using ToCharArray method in the string class. As of now, there exists no magic formula in the string class, which can directly provide an array of bytes. So, we are compelled to utilize the GetBytes function from the ASCII class, which accepts a character array and converts it into an array of bytes. This is an additional step that needs to be executed in the conversion process.

 

The last two lines of the above program merely verify whether all data has been supplied correctly or otherwise.

 

a.cs

public class zzz

{

public static void Main()

{

byte [] a;

a= abc("ABC");

string s = System.Text.Encoding.ASCII.GetString(a, 0, 3);

System.Console.WriteLine(s);

}

public static byte [] abc( string a)

{

byte [] b = new byte[a.Length];

System.Console.WriteLine(a.Length);

for ( int i = 0 ; i < a.Length ; i++)

{

System.Console.WriteLine(a[i]);

b[i] = (byte)a[i];

}

return b;

}

}

 

Output

3

A

B

C

ABC

 

In this program, we write our own function that accepts a string as a parameter, and returns an array of bytes. This function eliminates the intermediate step of converting the string into an array of chars, prior to its final conversion into an array of bytes.

 

The Length member in the string returns the length of the string, and also determines the size of the byte array, which is to be created. In the 'for' loop, every individual character can be accessed, one at a time, by using the indexer property of the string class. Concurrently, the byte array is also filled up. The need for a cast operator has been explained earlier. Finally, at the end of the function, we return this freshly filled up array, and thereafter, display the byte array. The array is displayed merely to corroborate that all actions have been executed as planned.

 

File Handling

 

a.cs

using System;

using System.IO;

class zzz

{

public static void Main()

{

FileStream f = new FileStream("c:\\z.txt", FileMode.Open, FileAccess.Read);

byte [] a;

a = new byte[512];

int b = f.Read(a, 0, 512);

Console.WriteLine(b);

string s = System.Text.Encoding.ASCII.GetString(a, 0, b);

Console.Write(s);

}     

}

 

z.txt

vijay

mukhi

 

Output

12

vijay

mukhi

 

We start by creating an object f, which is an instance of class FileStream. The function is assigned the name of the file that we want to work with, along with the mode in which it is to be opened. The two parameters, FileMode.Open and FileAccess.Read, are enums, which open a file for reading.

 

The Read function in the file stream reads the file. The function has 3 parameters:

     The first parameter is a byte array. The data from the file is to be read into this array.

     The second parameter is the offset position.

     The third parameter is the maximum number of bytes to be read.

 

Thus, the Read function shall attempt to read upto 512 bytes from the file, until it reaches the end of the file. Since the file contains only the words 'vijay mukhi', the output is 12, which represents the number of bytes read. This is followed by the data, i.e. vijay mukhi.

Let us now browse through the data sent by a web server.

 

a.cs

using System;

using System.Net;

using System.IO;

class zzz

{

public static void Main()

{

WebRequest r = WebRequest.Create("http://www.vijaymukhi.com");

WebResponse re = r.GetResponse();

Stream s = re.GetResponseStream();

Byte[] a = new Byte[51200];

int b = s.Read(a, 0, 51200);

Console.WriteLine(b);

Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));

}     

}

 

Output

1085

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"

"http://www.w3.org/TR/REC-html40/frame.dtd">

<html>

<head>

<title>Vijay Mukhi's Technology cornucopia</title>

</head>

<frameset frameborder="0" framespacing="0" border="0" cols="*" rows="97,*">

  <frame marginwidth="0" marginheight="0" src="heading.html" name="heading" noresize scrolling="no">

  <frameset frameborder="0" framespacing="0" border="0" cols="210,*" rows="*">

  <frameset frameborder="1" framespacing="0" border="0" cols="*" rows="0,*">

  <frame marginwidth="0" marginheight="0" src="code.html" name="code" noresize scrolling="no" frameborder="0">

  <frame marginwidth="5" marginheight="5" src="menu_empty.html" name="menu" noresize scrolling="auto" frameborder="0">

  </frameset>

  <frame marginwidth="5" marginheight="5" src="main.html" name="text" noresize>

  </frameset>

<noframes>

<p>The <code>NOFRAMES</code> element is to be used to give useful content to people with browsers that cannot display frames.  One example is Lynx, a text-based browser.</p>

</noframes>

</frameset>

</html>

 

We expect you to either have a web server installed on your machine, or at least be connected to the net.

 

Every machine on the internet is recognized by a name. For example. the Microsoft site is known as www.microsoft.com; the Java site on the net is known as www.javasoft.com; and my site is known as www.vijaymukhi.com. However, every machine is also known by the common name of 'localhost'. When the address specified in the browser is www.microsoft.com, the web server of that machine sends across an HTML file to the browser. The file that is sent across is either named index.html or default.html.

 

In the above program, we would like to simulate the functioning of a browser, i.e. we want to fetch an HTML file from a web server.

 

In the System.Net namespace, there is a class called WebRequest that has a static function called Create. This function requires the name of the machine that runs a web server. While reading this book, in case you are not connected to the Internet, you may use 'http://localhost/' as the name of the machine. Or else, you may connect to a site on the net such as www.vijaymukhi.com.

 

A WebRequest object r is returned by the Create function. The WebRequest class has another function called GetResponse, which returns a WebResponse object. The value returned is stored in a variable called re. Calling the method GetResponseStream from the WebResponse object returns a Stream object that can be utilized to read the HTML file sent across by the web server.

 

The file handling concepts, learnt a short while ago, may be applied here too. The interaction between our program and the web server is handled by two classes, i.e. WebRequest and WebResponse. We have no inkling as to what each class accomplishes.

 

In order to read the HTML file off the Stream object, we create a byte array having a size of 51200. Thereafter, we employ the Read function to read 51200 bytes, from the inception of the stream, into the array. The value returned by the Read function is indicative of the number of bytes that have been read off the stream. On displaying this value, the number obtained is less then 51200 (in our case it is 1085), and not 51200. Therefore, despite our having commanded the Read function to read upto 51200 bytes, it read only 1085 bytes. This is because it encountered an End Of File mark after having read 1085 bytes.

 

In case of an error, the WebResponse object displays a Status code. Few of the values that may be assigned to this Status code, are as follows:

     200 indicates 'success'.

     403 indicates 'permission denied'.

     404 indicates that the 'file name does not exist'.

 

Since no errors are visible in our case, we assume the status code to be 200.

 

Thereafter, the byte array is converted into a string, so that it can be displayed using the GetString function. Therefore, we see the first 1085 bytes of the HTML file, sent by the web server.

 

a.cs

using System;

using System.Net;

using System.IO;

class zzz

{

public static void Main()

{

WebRequest r = WebRequest.Create("http://www.yahoo.com");

WebResponse re = r.GetResponse();

Stream s = re.GetResponseStream();

Byte[] a = new Byte[51200];

int b = s.Read(a, 0, 51200);

while (b > 0)

{

Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));

b = s.Read(a, 0, 512);

System.Console.WriteLine(b);

}

}     

}

 

We are not in a position to estimate the file size of the HTML file, which has been sent by the web server. So, we take recourse to the 'while' loop in situations of incertitude or perplexity.

 

The Read function returns a zero, when there is no more data to be read from the stream. Thus, the 'while' loop terminates when the value of b becomes zero. Therefore, the last line of the output would always display a zero. In fact, we can regulate the number of bytes that we want the Read function to read off the stream. Here, we specify a value of 512. The framework enjoys the license to ignore this number.

 

In order to retrieve a specific file off the web server, such as a.html, we add the name of the file at the end of the URL. If the file appertains to the local hard disk, the Create function will be written as follows:

 

WebRequest r = WebRequest.Create("http://localhost/a.html");

 

The next query which is likely to vex our minds is: Where would the file a.html be stored on the hard disk? The answer to this depends upon the web server installed on the machine. In case of IIS or PWS from Microsoft, the file a.html would be placed in the sub-directory c:\inetpub\wwwroot. Reading a file sent by a web server is similar to reading a file from the local disk. Therefore, we do not have to discover two separate ways of reading the same thing.

 

 

 

a.cs

using System;

using System.Net;

using System.IO;

class zzz

{

public static void Main()

{

Uri u = new Uri("http://www.yahoo.com");

HttpWebRequest wr = (HttpWebRequest) WebRequest.Create(u);

AsyncCallback a = new AsyncCallback(abc);

IAsyncResult r = (IAsyncResult) wr.BeginGetResponse(a,wr);

Console.ReadLine();

}

static void abc(IAsyncResult ar)

{

HttpWebRequest r = (HttpWebRequest) ar.AsyncState;

HttpWebResponse re = (HttpWebResponse) r.EndGetResponse(ar);

Stream s = re.GetResponseStream();

Console.WriteLine("Status code: " + re.StatusCode);

Byte[] a = new Byte[51200];

int b = s.Read(a, 0, 51200);

while (b > 0)

{

Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));

b = s.Read(a, 0, 512);

System.Console.WriteLine(b);

}

}

}

 

The above program displays the same output as before, but with a difference. It follows a dissimilar method of writing code. In the previous example, the Read function waited till the data was received from the Internet. When we are connected to the Internet, this wait could be indefinite. This effectively would disable the program from proceeding any further. While awaiting data, the program cannot carry out any other activity. Thus, to avoid squandering of time and resources, we need a mechanism, by means of which, the program can be intimated about the receipt of data for the HTML file.

The static function Create, now accepts a URI as a parameter. To create this URI object, we pass the same URI string as a parameter to the constructor. The return value of this function is stored in a HttpWebRequest object, since this class is derived from the class WebRequest. This class has a function BeginGetResponse, which accepts two parameters.

 

The first is an AsyncCallback delegate, which requires the name of a function. In the C# world, a delegate simply stands-in for a function to be called.

 

The second parameter is a State object. Despite our not making any use of any state related information, we are not allowed to supply a null value. It is mandatory to supply a value, without which, an error is generated. Thus, we specify the object wr, of type HttpWebRequest, as the parameter. Under normal circumstances, we are required to create a State object and furnish it as a parameter.

 

After a certain time duration, data arrives from the web server, and the function abc gets called. This function is passed IAsyncResult as a parameter, which has a property AsyncState. Since this is an Object, we cast it to HttpWebRequest.

 

We require an HttpWebResponse object, in order to obtain a Stream object, which will be employed to read the file received from the web server. The HttpWebRequest class has a EndGetResponse function, which accepts an IAsyncResult as a parameter, and returns a HttpWebResponse object. This object, in turn, is derived from the HttpResponse class. This function terminates the asynchronous request. The remaining code is similar to the above program. We however, are at liberty to execute other tasks, while the file is being received. Normally, a separate thread runs the function abc in an asynchronous manner.

 

a.cs

using System;

using System.Net;

using System.IO;

class zzz

{

public static void Main()

{

Uri u = new Uri("http://www.yahoo.com");

HttpWebRequest w = (HttpWebRequest) WebRequest.Create(u);

IAsyncResult r = (IAsyncResult) w.BeginGetResponse(new AsyncCallback(abc),w);

Console.ReadLine();

}

static void abc(IAsyncResult ar)

{

HttpWebRequest r = (HttpWebRequest) ar.AsyncState;

HttpWebResponse re = (HttpWebResponse) r.EndGetResponse(ar);

int b = 0;

char[] a = new char[512];

StreamReader rd = new StreamReader(re.GetResponseStream(), System.Text.Encoding.UTF8);

StringWriter w = new StringWriter();

b = rd.Read(a, 0, 512);

while (b != 0 )

{

w.Write(a, 0, 512);

b= rd.Read(a, 0, 512);

}

Console.WriteLine("Message = " + w.ToString());

}

}

 

There are a myriad ways of achieving the same output. In our case, we have presented a program that displays the same output as before, by using a different set of classes. The variations are only in the function abc.

 

We first create an array of chars and not bytes. Thereafter, a StreamReader object rd, which is derived from TextReader, is created. This class has the ability to read characters from the Stream object supplied to it. The second parameter to the constructor is the encoding-type to be used, while reading character input from a stream. The Stream class is designed for byte input-output only. By default, it takes the UTF-8 encoding. Thus, we did not have to pass it as a parameter. It does not default to the ANSI code page for the current system. The advantage of UTF-8 is that, it handles Unicode characters appropriately, with localized versions of the operating system.

 

The StreamReader class is similar to the Stream class, in that, its Read function accepts an array of chars and not an array of bytes. The return value also remains the same.

 

Thereafter, the StringWriter class concatenates this series of chars. Thus, the StringWriter object w, stores the HTML file as one large string, which gets appended by the Write function.

 

We could also have used the WriteLine function to display the array of chars.

 

Domain Name System

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

String s = Dns.GetHostName();

System.Console.WriteLine(s);

}

}

 

Output

vmukhi

 

Whenever we need to connect to any computer, the machine's IP address has to be determined. Every machine on the Internet is identified by a unique number, which is called the machine's IP address. This number is of a 'long' data type. Since it is humanly impossible to memorize 4 billion numbers, every machine is also allotted a name. The system that maps a name to its corresponding IP address, is called the Domain Name System (DNS).

The class Dns contains only static entities, and hence, it is called a static class. One of its members is called GetHostName, which returns the name of the machine on which the current program is being executed.

 

As we mentioned earlier, every machine by default, is assigned the name of localhost. While installing Windows 2000, we have proactively assigned it the name of 'vmukhi'. Hence, the output shows 'vmukhi'.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main() {

IPHostEntry i = Dns.GetHostByName("www.microsoft.com");

IPAddress [] a = i.AddressList;

foreach ( IPAddress j in a)

System.Console.WriteLine(j.ToString()+ " " + j.Address);

}

}

 

Output

207.46.130.45 763506383

207.46.230.229 -437899569

207.46.131.91 1535323855

207.46.131.199 -947704113

207.46.230.219 -605671729

 

Every web server or client on the Internet is known by an IP address and a corresponding name. Moreover, the name on the Internet, such as, www.microsoft.com, may not necessarily be restricted to a single IP address. It may have multiple IP addresses mapped onto it. This is what the above program demonstrates.

 

The function GetHostByName is called and given the domain name, whose IP addresses are to be retrieved. An object is returned, which is an instance of IPHostEntry. This class has a property AddressList, which returns an array of IPAddress objects. A single object is assigned to each IP address represented by the domain name.

An IP address is a protracted number. It comprises of four numbers, each having a value, which falls within the range of 0 to 255. Thus, we can represent an IP address in the dotted decimal notation, where the four numbers are separated from each other by a dot. Displaying the Address member directly exhibits a long number. You can ascertain as to which of these is more readable. The 'foreach' statement is used to iterate through the array.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

IPHostEntry i = Dns.GetHostByName("www.microsoft.com");

String [] a = i.Aliases;

foreach ( String j in a)

System.Console.WriteLine(j);

}

}

 

Output

www.microsoft.com

 

An IP address may contain other DNS names, which resolve to the same address. In this case, there are none. Thus, it is evident that the Microsoft site on the net has no other aliases or names.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

IPHostEntry i = Dns.GetHostByName("www.microsoft.com");

String a = i.HostName;

System.Console.WriteLine(a);

}

}

Output

www.microsoft.akadns.net

 

The property HostName gives us the DNS name of the host. The HostName for www.microsoft.com happens to be www.microsoft.akadns.net.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

IPAddress i = IPAddress.Parse("207.46.130.45");

System.Console.WriteLine(i.ToString()+ " " + i.Address);

}

}

 

Output

207.46.130.45 763506383

 

At times, we may wish to convert a dotted decimal IP address to an int, i.e. an actual long number. The Parse function in IPAddress accepts a dotted decimal IP address as a string, and returns an object that is an instance of IPAddress. We then display the value as a string and an int.

 

How do we achieve the reverse process? We may want to convert an IP Address from a dotted decimal notation, into an instance of IP Address. At other times, we may want to convert an IPAddress, which is in the form of a string, into the dotted decimal notation. The same function can be used to accomplish both these predilections.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

String s = Dns.IpToString(763506383);

System.Console.WriteLine(s);

}

}

 

Output

207.46.130.45

 

The IP addresses are capable of being converted from an int format into a dotted decimal string or quad representation, by using the IpToString function.

 

a.cs

using System;

using System.Net;

class zzz

{

public static void Main()

{

IPHostEntry i = Dns.Resolve("www.microsoft.com");

System.Console.WriteLine(i.AddressList[0].ToString());

}

}

 

Output

207.46.230.229

 

The function Resolve in the Dns class, has no complications. We may be interested in an IP address, representing either a single domain name or multiple domain names. One of the solutions is, to use the function GetHostByName. Alternatively, the Resolve function may be used to achieve the same output.

 

Servers and Clients

 

Server program

 

a.cs

using System;

using System.Net;

using System.Net.Sockets;

using System.Text;

class zzz

{

public static void Main()

{

DateTime n;   

String s1; 

TcpListener t = new TcpListener(13);

t.Start();

while (true)

{

System.Console.WriteLine("Before");

Socket s = t.AcceptSocket();

System.Console.WriteLine("After");

n = DateTime.Now;

s1 = n.ToShortDateString() + " " + n.ToLongTimeString();

Byte[] b = Encoding.ASCII.GetBytes(s1.ToCharArray());

s.Send(b, b.Length, 0);

Console.WriteLine("Sent " + s1);

}

}

}

 

Client program

 

b.cs

using System;

using System.Net;

using System.Net.Sockets;

using System.IO;

using System.Text;

class zzz

{

public static void Main(String[] args)

{

TcpClient t = new TcpClient();

Byte[] r = new Byte[32];

t.Connect("127.0.0.1", 13);

Stream s = t.GetStream();

int n = s.Read(r, 0, r.Length);

String a = Encoding.ASCII.GetString(r);

Console.WriteLine("Received " + n );

Console.WriteLine(a);

t.Close();

}

}

 

Server Dos box

Before

After

Sent 09/13/2001 17:35:48

Before

 

Client Dos box

Received 20

09/13/2001 17:35:48

 

We have supplied the following two programs, which are to be executed in two different DOS boxes:

     a TCP server program.

     a TCP client program.

 

The Server is to be executed first, since the client expects the server to be running. So, lets understand the server program first.

 

The DateTime class provides access to the current date and time, in a multitude of formats. We first create an object, which is an instance of the TcpListener class. Thereafter, the constructor is passed a parameter of 13. It has no other significance, other than being called a Port Number.

 

Most people, when online, either surf the net, or download files, or send E-mail. These activities have certain rules/protocols that are required to be followed for successful execution of the task. We cannot have separate machines to handle each of the above protocols. Therefore, the programs/servers are executed on the same machine; however, they work on a specified number called, the Port Number.

 

Every packet of data is tagged with a number, which specifies the protocol that the packet belongs to. There is a world-wide body called IANA (Internet Assigned Numbers Authority), which determines the number to be assigned to each protocol. Some of them are as follows:

     The World Wide Web has been allocated the number 80

     FTP has been allocated the number 21

     E-mail has been allocated the number 25.

 

Thus, if we notice a packet with a port number 80, we would immediately understand that the packet is carrying data for the World Wide Web, i.e. the www protocol.

 

The TcpListener class constructor requires the Port Number, since our server only accepts packets that carry this number. We have asked our server to listen, and accept packets on port 13. The first 1024 port numbers are reserved by IANA for various protocols. The rest of the numbers may be utilized by us, for our own protocols. The number 13 falls within the range of reserved port numbers.

 

TCP stands for Transmission Control Protocol. Every TCP server needs a function to kick start the entire process. This function is called 'Start'. As of now, we are neither aware of, nor care about what 'start' actually does. We are only apprehensive and aware about the fact that, if this function is not called, none of the clients would ever be able to connect to our server. The TcpListener class is used extensively while writing an Internet server.

 

Around 20 years ago, a large number of networking protocols existed. A networking protocol merely consists of a series of numbers in a specific format, which two machines transmit to each other, in order to facilitate communication. It was extremely complex and cumbersome for programmers to comprehend the numerous networking protocols. TCP/IP was also one such protocol. At that time, nobody would have known that, it would turn out to be the world's most dominant networking protocol, in the days to come.

 

A group of programmers banded together and came out with a series of functions, which could generate the bytes for a large number of networking protocols. For reasons unknown, they named these functions as Sockets API. Thus, a networking coder is traditionally known as a Sockets programmer.

In the case of C#, we have a class called Socket. TcpListener is derived from it. The advantage of using socket classes like TCPListener is that, functions like 'Start', call a tremendous amount of code from other Sockets classes. So, we do not have to delve into the innards of sockets programming, in order to be able to use them. The TCPListener class, in a sense, provides us with networking services at a higher level of abstraction, than what could have been achieved, by using the Socket class directly.

 

On running our server ‘a’, we only notice a single word 'Before'. Thereafter, our server seems to wait forever. This wait is attributable to the Accept function. In computer parlance, it goes off to sleep. Our server waits till some TCP client connects to our server on port 13. This wait could extend till eternity.

 

The time has come to try and comprehend as to what a TCP client program is.

 

The TCPClient class is similar to a TcpListener class. This class has functions that use the code from Sockets classes and offer assistance in writing a TCP client program.

 

The Connect function connects to a server. Therefore, it requires the domain name or IP address of the machine, which we desire to connect to. Further, the port number also needs to be specified. We have specified the port number as 13. This is because our server only accepts packets marked with the port number 13. When the Connect function attempts to connect to the server, the server moves beyond the Accept function.

 

The function Accept in the server, returns an instance of a Socket class. This object now becomes a handle, and is used to return data back to the client. The DateTime class has a static member called 'Now', which gives the current date and time. Since the value is a DateTime object, we use one of the many functions in the DateTime class, to display the date as a string in a specific format. Here, we use two functions, ToShortDateString and ToLongDateString; and store the concatenated string value in a variable s1.

 

 

This string is now required to be sent across to the client. To accomplish this, we need to convert the date in the string, into an array of bytes. So, we first convert the string s1 to an array of chars, and then, employ the GetBytes function to convert this array of chars into an array of bytes. The socket class has a function called 'Send'. It accepts a byte array as the first parameter and the length of the array as the second parameter; and eventually sends the data to the client.

 

At the client end, the GetStream function is used to obtain a Stream object. Then, using the good old Read function, the bytes in the Stream are read into a byte array. Finally, the byte array is converted into a string, and then its contents are displayed.

 

In one of our other C# books, we had demonstrated how the Web client connects to the Webserver on port 80. This program had used a set of classes distinct from the ones used here. But, in the ultimate analysis, they all call code from the Sockets class.