Use C # to develop your own web server (figure)

Source: Internet
Author: User
Tags socket error

Summary

This article discusses how to use C # To develop a simple web server application. Although we can use any programming language that supports. net, I chose C #. The code in this article is compiled using Microsoft's Visual C # compiler version 7.00.9254 [CLR version v1.0.2914] of Beta 2. After some minor changes to the Code, beta 1 may also be compiled. The Web server application can run on the same server as IIS or any other web server software. You only need to specify an idle port for the application. In this article, I also assume that the reader has a certain understanding of. net, C #, or Visual Basic. net.

The Web server application can return HTML files to the browser and supports images. It does not load embedded images or supports any scripting language. For simplicity, I develop it into a command line application.

Preparations

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; create three files in the data directory, for example:

Mimes. dat
Vdirs. dat
Default. dat

Mime. dat contains 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 virtual directory information in the format of; <physical directory>, for example:

/; C:/mywebserverroot/
Test/; C:/mywebserverroot/Imtiaz/

Default. dat contains the file information in the virtual directory, for example:

Default.html
Default.htm
Index.html
Index.htm

For simplicity, we will use text files to store all the information, but we can also use other formats such as XML. Before studying the code, let's take a look at the header information that the browser needs to pass when logging on to the website.

Let's take the request test.html as an example. Enter http: // localhost: 5050/test.html in the address bar of the browser (remember 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: 5050 connection: 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; // You can select any idle port.
// The builder that generates the tcplistener starts to listen to the given port and starts a thread that calls the startlisten () method.
Public MyWebServer ()
{
Try
{
// Start listening for the specified 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 ("an exception occurred while listening:" + E. tostring ());
}
}

We have defined the namespace, including necessary references for the application, initialized the port in the builder, started the port listening process, and created a new thread to call the startlisten function.

Assume that the user does not provide a file name in the URL. In this case, we must determine the default file name and return it to the browser, just like defining 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 getthedefafilfilename function uses the directory path as the input parameter, opens the default. dat file, searches for the file in the directory, and returns the file name or a space based on whether the file is found.

Public String getthedefafilfilename (string slocaldirectory)
{
Streamreader SR;
String Sline = "";
Try
{
// Open default. dat to obtain the default list
Sr = new streamreader ("Data // default. dat ");
While (Sline = Sr. Readline ())! = NULL)
{
// Search for 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. Remember that the file format is important in any situation.

Public String getlocalpath (string smywebserverroot, string sdirname)
{
Treamreader SR;
String Sline = "";
String svirtualdir = "";
String srealdir = "";
Intistartpos = 0;
// Delete unnecessary Spaces
Sdirname. Trim ();
// Convert to lowercase
Smywebserverroot = smywebserverroot. tolower ();
// Convert to lowercase
Sdirname = sdirname. tolower ();
Try
{
// Open the vdirs. dat file to obtain the virtual directory
Sr = new streamreader ("Data // vdirs. dat ");
While (Sline = Sr. Readline ())! = NULL)
{
// Delete unnecessary Spaces
Sline. Trim ();
If (Sline. length> 0)
{
// Locate the delimiter
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 use the file extension provided by the user to determine the MIME type.
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 to obtain the virtual directory
Sr = new streamreader ("Data // mime. dat ");
While (Sline = Sr. Readline ())! = NULL)
{
Sline. Trim ();
If (Sline. length> 0)
{
// Locate the delimiter
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 "";
}

Next, we will compile 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 MIME type is not provided, set it 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 with a large 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 components for writing an internet server application. Next we will discuss the key functions in the internet server application.
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 [1, 1024];
Int I = mysocket. Receive (breceive, breceive. length, 0 );
// Convert byte data to a string
String sbuffer = encoding. ASCII. getstring (breceive );
// Forward, we will only process the get type
If (sbuffer. substring (0, 3 )! = "Get ")
{
Console. writeline ("only get method is supported ..");
Mysocket. Close ();
Return;
}
// Search for HTTP requests
Istartpos = sbuffer. indexof ("HTTP", 1 );
// Obtain the "HTTP" text and version number. For example, it returns "HTTP/1.1"
String shttpversion = sbuffer. substring (istartpos, 8 );
// Parse the request type and directory/File
Srequest = sbuffer. substring (0, istartpos-1 );
// If a/Symbol exists, use/replace it
Srequest. Replace ("//","/");
// If the provided file name does not contain/, it indicates that this is a directory. To solve the problem, we need to find the default file name.
If (srequest. indexof (".") <1 )&&(! Srequest. endswith ("/")))
{
Srequest = srequest + "/";
}
// Parse the request file name
Istartpos = srequest. lastindexof ("/") + 1;
Srequestedfile = srequest. substring (istartpos );
// Parse the directory name
Sdirname = srequest. substring (srequest. indexof ("/"), srequest. lastindexof ("/")-3 );
The above Code does not need to be explained. It receives user requests, converts user requests from byte data to string data, and then searches for the request type, parse the HTTP Version Number, file, and directory information.
// Determine the physical directory
If (sdirname = "/")
Slocaldir = smywebserverroot;
Else
{
// Obtain the virtual directory
Slocaldir = getlocalpath (smywebserverroot, sdirname );
}
Console. writeline ("directory requested:" + slocaldir );
// If the physical directory does not exist, the 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 );
// Send information to the browser
Sendtobrowser (serrormessage, ref mysocket );
Mysocket. Close ();
Continue;
}

Suggestion: Microsoft's IE browser usually displays an HTTP Error webpage with a "friendly" point. If you want to display the error information of our web server application, you need to disable the "show friendly HTTP Error information" function in IE by clicking "Tools"> "Internet tools" in sequence ", this option is displayed in the "advanced" tab.

If the user does not provide the directory name, the web server application will use the getlocalpath function to obtain information about the physical directory. If the directory does not exist (or is not mapped to vdir. in dat) to send an error message to the browser. The Web server application determines the file name. If the user does not provide a file name, the web server application can call the getthedefafilename function to obtain the file name. If an error occurs, the error message is sent to the browser.

// If the file name does not exist, search for the default file list
If (srequestedfile. Length = 0)
{
// Get the default file name
Srequestedfile = getthedefadefafilename (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 will identify the MIME type:

String smimetype = getmimetype (srequestedfile );
// Construct the physical path
Sphysicalfilepath = slocaldir + srequestedfile;
Console. writeline ("file requested:" + sphysicalfilepath );
The last 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 the 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, "200 OK", ref mysocket );
Sendtobrowser (bytes, ref mysocket );
// Mysocket. Send (bytes, bytes. length, 0 );
}
Mysocket. Close ();
}
}
}
}
}

Compile and execute

You can use the following command to compile our web server application:

In my use. in the. NET development tool, you do not need to specify the name of any library.. NET development tool, you may need to use the/R parameter to add references to the dll library file.

To run the Web server application, just enter the program name and press Enter.

Now, let say user send the request, our web server will identify the default file name and sends to the browser.

Now, if the user sends a request, our web server application will decide to use the default file and return it to the browser. As shown in:

Of course, users can also request image files

Possible improvements

Webserver can be improved in many ways. It does not support embedded images and scripts. Readers can write their own ISAPI filters or use iis isapi filters.

Conclusion

This article demonstrates the basic principles of Web server development. We can still make many improvements to the web server application in this article, hoping that it can serve as an inspiring tool and enlighten readers.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.