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.