Web|web Service |web Server
Summary
This article discusses how to use C # to develop a simple Web server application. Although we can use any kind of support. NET programming language development, but I chose C #. The code in this article is compiled using Microsoft's β2 version of Visual C # Compiler version 7.00.9254 [CLR Version v1.0.2914], with minor changes to the code, which can also be compiled with the β1 version. The Web server application can run on a single server with IIS or any other Web server software, as long as you specify an idle port for it. In this article, I also assume that the reader is in. NET, C #, or Visual Basic. NET has a certain understanding.
The Web server application can return HTML-formatted files to the browser, and it supports images that do not load embedded images or support any scripting language. For the sake of simplicity, I developed it into a command-line application.
Preparatory work
First, we need to define a root folder for this Web server application, for example, C:\MyPersonalwebServer, and then create a data directory under the root directory, for example, C:\MyPersonalwebServer\Data ; Finally, create three files in the data directory, for example:
Mimes.Dat
Vdirs.Dat
Default.Dat
Mime.Dat will contain the MIME types supported by the Web server in the format of < extension >; For example:
. html;text/html
. htm;text/html
. bmp;image/bmp
VDirs.Dat contains the information of the virtual directory, the format is; < physical directory, for example:
/; c:\mywebserverroot/
test/; C:\myWebServerRoot\Imtiaz\
The Default.Dat contains information about the files in the virtual directory, such as:
Default.html
Default.htm
Index.html
Index.htm
For simplicity, we'll use a text file to store all the information, but we can also use other formats such as XML. Before we start studying the code, let's take a look at the header information that the browser needs to deliver when you log on to the site.
We take the request test.html as an example to explain. In the browser's address bar, enter http://localhost: 5050/test.html (remember that you need to include the port number in the URL), the server will get the following information:
〈/drive:\physicaldir〉
Get/test.html http/1.1
Accept:image/gif, Image/x-xbitmap, Image/jpeg, Image/pjpeg, Application/vnd.ms-powerpoint, application/vnd.ms-excel , Application/msword, */*
Accept-language:en-usaccept-encoding:gzip, deflate
user-agent:mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0;. NET CLR 1.0.2914)
Host:localhost:5050connection:keep-alive
Start programming
Namespace Imtiaz
{
Using System;
Using System.IO;
Using System.Net;
Using System.Net.Sockets;
Using System.Text;
Using System.Threading;
Class MyWebServer
{
Private TcpListener MyListener;
private int port = 5050; Free ports are optional
The builder that generates the TcpListener starts listening on the given port, and it initiates a thread that calls the Startlisten () method
Public MyWebServer ()
{
Try
{
Start listening on a given port
MyListener = new TcpListener (port);
Mylistener.start ();
Console.WriteLine ("Web Server Running ... Press ^c to Stop ... ");
Start the thread that calls the Startlisten method
Thread th = new Thread (new ThreadStart (Startlisten));
Th. Start ();
}
catch (Exception e)
{
Console.WriteLine ("A Exception occurred while listening:" +e.tostring ());
}
}
We defined the namespace, including the necessary references to the application, initialized the port in the builder, started the port listener process, and created a new thread to call the Startlisten function.
We assume that the user does not provide a filename in the URL, in which case we must determine the default file name ourselves and return it to the browser, as defined by the default document in the document label in IIS.
We have stored the default file name in Default.dat and stored the file in the data directory. The Getthedefaultfilename function takes the directory path as an input parameter, opens the Default.dat file, finds the file in the directory, and returns a filename or a space based on whether the file was found.
public string Getthedefaultfilename (string slocaldirectory)
{
StreamReader SR;
String sline = "";
Try
{
Open Default.dat and get the default list
sr = new StreamReader ("Data\\default.dat");
while (sline = Sr. ReadLine ())!= null)
{
Find missing files in the root directory of the Web server
if (file.exists (slocaldirectory + sline) = = True)
Break
}
}
catch (Exception e)
{
Console.WriteLine ("An Exception occurred:" + e.tostring ());
}
if (file.exists (slocaldirectory + sline) = = True)
Return sline;
Else
Return "";
}
As in IIS, we must resolve the virtual directory to a physical directory. In Vdir.Dat, we have stored the image relationship between the actual physical directory and the virtual directory. It is important to remember that in any case, the format of the file is significant.
public string Getlocalpath (string smywebserverroot, String sdirname)
{
Treamreader SR;
String sline = "";
String svirtualdir = "";
String srealdir = "";
Intistartpos = 0;
Remove extra spaces
Sdirname.trim ();
Convert to lowercase
Smywebserverroot = Smywebserverroot.tolower ();
Convert to lowercase
Sdirname = Sdirname.tolower ();
Try
{
Open the Vdirs.dat file and get the virtual directory
sr = new StreamReader ("Data\\vdirs.dat");
while (sline = Sr. ReadLine ())!= null)
{
Remove extra spaces
Sline.trim ();
if (Sline.length > 0)
{
Find the split character
Istartpos = Sline.indexof (";");
Convert to lowercase
sline = Sline.tolower ();
Svirtualdir = sline.substring (0,istartpos);
Srealdir = sline.substring (Istartpos + 1);
if (Svirtualdir = = sdirname)
{
Break
}
}
}
}
catch (Exception e)
{
Console.WriteLine ("An Exception occurred:" + e.tostring ());
}
if (Svirtualdir = = sdirname)
return srealdir;
Else
Return "";
}
We must also determine the MIME type using the user-supplied file name extension.
public string GetMimeType (string srequestedfile)
{
StreamReader SR;
String sline = "";
String smimetype = "";
String sfileext = "";
String smimeext = "";
Convert to lowercase
Srequestedfile = Srequestedfile.tolower ();
int istartpos = Srequestedfile.indexof (".");
Sfileext = srequestedfile.substring (Istartpos);
Try
{
Open the Vdirs.dat file and get the virtual directory
sr = new StreamReader ("Data\\mime.dat");
while (sline = Sr. ReadLine ())!= null)
{
Sline.trim ();
if (Sline.length > 0)
{
Find the split character
Istartpos = Sline.indexof (";");
Convert to lowercase
sline = Sline.tolower ();
Smimeext = sline.substring (0,istartpos);
Smimetype = sline.substring (Istartpos + 1);
if (Smimeext = = Sfileext)
Break
}
}
}
catch (Exception e)
{
Console.WriteLine ("An Exception occurred:" + e.tostring ());
}
if (Smimeext = = Sfileext)
return smimetype;
Else
Return "";
}
Let's write a function to create and send header information to the browser (client).
public void Sendheader (string shttpversion,
String Smimeheader,
int Itotbytes,
String Sstatuscode,
Ref Socket Mysocket)
{
String sbuffer = "";
If the user does not provide a MIME type, it is set to text/html by default
if (smimeheader.length = 0)
{
Smimeheader = "text/html"; Default Mime Type is text/html
}
Sbuffer = sbuffer + shttpversion + sstatuscode + "\ r \ n";
Sbuffer = Sbuffer + "server:cx1193719-b\r\n";
Sbuffer = Sbuffer + "Content-type:" + Smimeheader + "\ r \ n";
Sbuffer = Sbuffer + "accept-ranges:bytes\r\n";
Sbuffer = Sbuffer + "Content-length:" + itotbytes + "\r\n\r\n";
byte[] Bsenddata = Encoding.ASCII.GetBytes (sbuffer);
Sendtobrowser (Bsenddata, ref mysocket);
Console.WriteLine ("Total Bytes:" + itotbytes.tostring ());
}
The Sendtobrowser function sends information to the browser, which is a function of a larger workload.
public void Sendtobrowser (String sData, ref Socket Mysocket)
{
Sendtobrowser (Encoding.ASCII.GetBytes (sData), ref mysocket);
}
public void Sendtobrowser (byte[] bsenddata, ref Socket Mysocket)
{
int numbytes = 0;
Try
{
if (mysocket.connected)
{
if ((numbytes = Mysocket.send (Bsenddata, bsenddata.length,0)) = = 1)
Console.WriteLine ("Socket Error cannot Send Packet");
Else
{
Console.WriteLine ("No.") of bytes Send {0} ", numbytes);
}
}
Else
Console.WriteLine ("Connection dropped ...");
}
catch (Exception e)
{
Console.WriteLine ("Error occurred: {0}", e);
}
}
We already have some parts for writing an Internet server application, and we'll discuss the key functions in Internet server applications.
public void Startlisten ()
{
int istartpos = 0;
String srequest;
String Sdirname;
String Srequestedfile;
String Serrormessage;
String Slocaldir;
String smywebserverroot = "c:\\mywebserverroot\\";
String Sphysicalfilepath = "";
String sformattedmessage = "";
String sresponse = "";
while (true)
{
Accept a new connection
Socket mysocket = Mylistener.acceptsocket ();
Console.WriteLine ("Socket Type" +mysocket.sockettype);
if (mysocket.connected)
{
Console.WriteLine ("\nclient connected!! \n==================\n
CLient IP {0}\n ", mysocket.remoteendpoint);
Generate a byte array to receive data from the client
byte[] breceive = new byte[1024];
int i = mysocket.receive (breceive,breceive.length,0);
Convert byte data to string
String sbuffer = Encoding.ASCII.GetString (breceive);
Forward we will only deal with get types
if (sbuffer.substring (0,3)!= "get")
{
Console.WriteLine (' Only get ' is supported ... ');
Mysocket.close ();
Return
}
Find HTTP Requests
Istartpos = Sbuffer.indexof ("HTTP", 1);
Gets the "HTTP" text and version number, for example, it returns "http/1.1"
String shttpversion = Sbuffer.substring (istartpos,8);
Resolves the requested type and directory/file
Srequest = sbuffer.substring (0,istartpos-1);
If there is a \ symbol, use/replace
Srequest.replace ("\", "/");
If there is no/in the filename provided, this is a directory and we need to find the default file name
if ((Srequest.indexof (".") <1) && (!srequest.endswith ("/"))
{
Srequest = srequest + "/";
}
Resolves the file name of the request
Istartpos = Srequest.lastindexof ("/") + 1;
Srequestedfile = srequest.substring (Istartpos);
Resolving directory names
Sdirname = srequest.substring (Srequest.indexof ("/"), Srequest.lastindexof ("/")-3);
The code above needs no explanation, it receives the user's request, converts the user's request from byte data to string data, and then looks for the type of request, parsing the version number, file, and directory information of the HTTP.
Determining the physical directory
if (Sdirname = = "/")
Slocaldir = Smywebserverroot;
Else
{
Get virtual Directory
Slocaldir = Getlocalpath (Smywebserverroot, sdirname);
}
Console.WriteLine ("Directory requested:" + Slocaldir);
If the physical directory does not exist, an error message is displayed
if (slocaldir.length = 0)
{
Serrormessage = "〈h2〉error!! Requested Directory does not exists〈/h2〉〈br〉 ";
Serrormessage = serrormessage + "Please check Data\\vdirs.dat";
Format the information
Sendheader (Shttpversion, "", serrormessage.length, "404 Not Found", ref mysocket);
Sending information to the browser
Sendtobrowser (Serrormessage, ref mysocket);
Mysocket.close ();
Continue
}
Hint: Microsoft's IE browser will always show a more "friendly" HTTP error page, if you want to display our Web server application error message, we need to disable IE "Show friendly HTTP error message" function, the method is to click "Tool"-> " Internet Tools, and you can see this option in the Advanced tab.
If the user does not provide a directory name, the Web server application uses the Getlocalpath function to obtain information about the physical directory, and if the directory does not exist (or is not mapped to an entry in Vdir.Dat), an error message is sent to the browser. The Web server application then determines the file name, and if the user does not provide a filename, the Web server application can call the Getthedefaultfilename function to get the file name, and if there is an error, the error message is sent to the browser.
If the filename does not exist, find the default file list
if (srequestedfile.length = 0)
{
Get the default filename
Srequestedfile = Getthedefaultfilename (Slocaldir);
if (Srequestedfile = "")
{
Serrormessage = "〈h2〉error!! No Default File Name specified〈/h2〉 ";
Sendheader (Shttpversion, "", serrormessage.length, "404 Not Found",
Ref mysocket);
Sendtobrowser (Serrormessage, ref mysocket);
Mysocket.close ();
Return
}
}
Here we identify the MIME type:
String Smimetype = GetMimeType (srequestedfile);
Building a physical path
Sphysicalfilepath = Slocaldir + srequestedfile;
Console.WriteLine ("File requested:" + Sphysicalfilepath);
The final step is to open the requested file and send it to the browser.
if (file.exists (sphysicalfilepath) = = False)
{
Serrormessage = "〈h2〉404 error! File does not exists...〈/h2〉 ";
Sendheader (Shttpversion, "", serrormessage.length, "404 Not Found", ref mysocket);
Sendtobrowser (Serrormessage, ref mysocket);
Console.WriteLine (Sformattedmessage);
}
Else
{
int itotbytes=0;
Sresponse = "";
FileStream fs = new FileStream (Sphysicalfilepath, FileMode.Open,FileAccess.Read,
FileShare.Read);
Create a reader that can read byte data from FileStream
BinaryReader reader = new BinaryReader (FS);
byte[] bytes = new Byte[fs. Length];
int read;
while (read = reader. Read (bytes, 0, bytes. Length))!= 0)
{
Read data from a file and send the data to the network
Sresponse = Sresponse + Encoding.ASCII.GetString (bytes,0,read);
Itotbytes = itotbytes + read;
}
Reader. Close ();
Fs. Close ();
Sendheader (Shttpversion, Smimetype, itotbytes, "OK", ref mysocket);
Sendtobrowser (bytes, ref mysocket);
Mysocket.send (bytes, bytes. length,0);
}
Mysocket.close ();
}
}
}
}
}
Compiling and executing
You can compile our Web server application using the commands shown in the following illustration:
In my use. NET development tools, there is no need to specify any library name in the older version of the. NET development tool, you may need to add a reference to a DLL library file using the/R parameter.
To run the Web server application, enter the name of the program as shown in the following figure and press ENTER.
Now, let say user send the request, our web server would identify the default file name and sends to the browser.
Now that we assume that the user sent the request, our Web server application will decide to use the default file and return it to the browser. As shown in the following illustration:
Of course, the user can also request an image file
Possible improvements
There are still many areas where webserver can be improved. It does not support embedded images and scripts, readers can write ISAPI filters themselves, or you can use IIS ISAPI filters.
Conclusion
This article shows the basics of developing a Web server, and we can still make a lot of improvements to the Web server application in this article, hoping it will be a useful inspiration to readers.