Click here to Skip to main content
11,928,587 members (47,324 online)
Click here to Skip to main content
Add your own
alternative version


170 bookmarked

Create your own Web Server using C#

, 27 Oct 2001
Rate this:
Please Sign up or sign in to vote.
A step by step introduction to writing a Web Server using C#.


This article explains how to write a simple web server application using C#. Though it can be developed in any .NET supported language, I chose C# for this example. The code is compiled using beta2. Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR version v1.0.2914]. It can be used with Beta1 with some minor modification. This application can co-exists with IIS or any web server, the key is to choose any free port. I assume that the user has some basic understanding of .NET and C# or VB.Net. This Web server just returns html formatted files and also supports images. It does not supports any kind of scripting. I have developed a console-based application for simplicity.

The Web Server

First we will define the root folder for our web server. Eg: C:\MyPersonalwebServer, and will create a Data directory underneath, our root directory Eg: C:\MyPersonalwebServer\Data. We will Create three files under data directory i.e.

Mimes.Dat Vdirs.Dat Default.Dat

Mime.Dat will have the mime type supported by our web server, Syntax:  <EXTENSION>; <MIME Type>

.html; text/html .htm; text/html .gif; image/gif .bmp; image/bmp

VDirs.Dat will have the virtual directory Information. Syntax: <VirtualDir>; <PhysicalDir>  

/; C:\myWebServerRoot/ /test/; C:\myWebServerRoot\Imtiaz\

Note: We have to include all the directories used by our web server, for example, if the html page contains a reference to images and we want to display image, we need to include it also. e.g.
/images/; c:myWebServerRoot\Images\

Default.Dat will have the virtual directory Information; 

default.html default.htm Index.html Index.htm;

We will store all the information in plain text file for simplicity, we can use XML, registry or even hard code it. Before proceeding to our code let us first look the header information which browser will pass while requesting for our web site

Let say we request test.html.  We type http://localhost:5050/test.html (Remember to include port in the url),  Here is what the web server gets.

GET /test.html HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, 
        application/, application/, 
        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

Let us dive into the code.

// MyWebServer Written by Imtiaz Alam
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 ;  // Select any free port you wish 

     //The constructor which make the TcpListener start listening on th
     //given port. It also calls a Thread on the method StartListen(). 
    public MyWebServer()
             //start listing on the given port
            myListener = new TcpListener(port) ;
            Console.WriteLine("Web Server Running... Press ^C to Stop...");
             //start the thread which calls the method 'StartListen'
            Thread th = new Thread(new ThreadStart(StartListen));
            th.Start() ;

        catch(Exception e)
            Console.WriteLine("An Exception Occurred while Listening :" 
                               + e.ToString());

We defined a namespace, included the references required in our application and initialized the port in the constructor, started the listener and created a new thread and called the startlisten function.

Now let us assume that the user does not supply the file name, in that case we have to identify the default filename and send it to the browser. As in IIS we define the default document under documents tab.

We have already stored the default file name in the default.dat and stored it in the data directory. The GetTheDefaultFileName function takes the directory path as input, open the default.dat file and looks for the file in the directory provided and returns the file name or blank depends on the situation.

public string GetTheDefaultFileName(string sLocalDirectory)
    StreamReader sr;
    String sLine = "";

        //Open the default.dat to find out the list
        // of default file
        sr = new StreamReader("data\\Default.Dat");

        while ((sLine = sr.ReadLine()) != null)
            //Look for the default file in the web server root folder
            if (File.Exists( sLocalDirectory + sLine) == true)
    catch(Exception e)
        Console.WriteLine("An Exception Occurred : " + e.ToString());
    if (File.Exists( sLocalDirectory + sLine) == true)
        return sLine;
        return "";

We also need to resolve the virtual directory to the actual physical directory like we do in IIS. We have already stored the mapping between the Actual and Virtual directory in Vdir.Dat. Remember in all the cases the file format is very important.

public string GetLocalPath(string sMyWebServerRoot, string sDirName)

    StreamReader sr;
    String sLine = "";
    String sVirtualDir = ""; 
    String sRealDir = "";
    int iStartPos = 0;

    //Remove extra spaces

    // Convert to lowercase
    sMyWebServerRoot = sMyWebServerRoot.ToLower();

    // Convert to lowercase
    sDirName = sDirName.ToLower();

        //Open the Vdirs.dat to find out the list virtual directories
        sr = new StreamReader("data\\VDirs.Dat");

        while ((sLine = sr.ReadLine()) != null)
            //Remove extra Spaces

            if (sLine.Length > 0)
                //find the separator
                iStartPos = sLine.IndexOf(";");

                // Convert to lowercase
                sLine = sLine.ToLower();

                sVirtualDir = sLine.Substring(0,iStartPos);
                sRealDir = sLine.Substring(iStartPos + 1);

                if (sVirtualDir == sDirName)
    catch(Exception e)
        Console.WriteLine("An Exception Occurred : " + e.ToString());

    if (sVirtualDir == sDirName)
        return sRealDir;
        return "";

We also need to identify the MIME type, using the file extension supplied by the user

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);

        //Open the Vdirs.dat to find out the list virtual directories
        sr = new StreamReader("data\\Mime.Dat");

        while ((sLine = sr.ReadLine()) != null)


            if (sLine.Length > 0)
                //find the separator
                iStartPos = sLine.IndexOf(";");

                // Convert to lower case
                sLine = sLine.ToLower();

                sMimeExt = sLine.Substring(0,iStartPos);
                sMimeType = sLine.Substring(iStartPos + 1);

                if (sMimeExt == sFileExt)
    catch (Exception e)
        Console.WriteLine("An Exception Occurred : " + e.ToString());

    if (sMimeExt == sFileExt)
        return sMimeType; 
        return "";

Now we will write the function, to build and sends header information to the browser (client)

public void SendHeader(string sHttpVersion, string sMIMEHeader, 
            int iTotBytes, string sStatusCode, ref Socket mySocket)

    String sBuffer = "";
    // if Mime type is not provided set default to text/html
    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. This is an overloaded function.

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;
        if (mySocket.Connected)
            if (( numBytes = mySocket.Send(bSendData, 
                  bSendData.Length,0)) == -1)
                Console.WriteLine("Socket Error cannot Send Packet");
                Console.WriteLine("No. of bytes send {0}" , numBytes);
            Console.WriteLine("Connection Dropped....");
    catch (Exception  e)
        Console.WriteLine("Error Occurred : {0} ", e );

We now have all the building blocks ready, now we will delve into the key function of our 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 = "";

        //Accept a new connection
        Socket mySocket = myListener.AcceptSocket() ;

        Console.WriteLine ("Socket Type " + mySocket.SocketType ); 
            Console.WriteLine("\nClient Connected!!\n==================\n
             CLient IP {0}\n", mySocket.RemoteEndPoint) ;

            //make a byte array and receive data from the client 
            Byte[] bReceive = new Byte[1024] ;
            int i = mySocket.Receive(bReceive,bReceive.Length,0) ;

            //Convert Byte to String
            string sBuffer = Encoding.ASCII.GetString(bReceive);

            //At present we will only deal with GET type
            if (sBuffer.Substring(0,3) != "GET" )
                Console.WriteLine("Only Get Method is supported..");

            // Look for HTTP request
            iStartPos = sBuffer.IndexOf("HTTP",1);

            // Get the HTTP text and version e.g. it will return "HTTP/1.1"
            string sHttpVersion = sBuffer.Substring(iStartPos,8);

            // Extract the Requested Type and Requested file/directory
            sRequest = sBuffer.Substring(0,iStartPos - 1);

            //Replace backslash with Forward Slash, if Any

            //If file name is not supplied add forward slash to indicate 
            //that it is a directory and then we will look for the 
            //default file name..
            if ((sRequest.IndexOf(".") <1) && (!sRequest.EndsWith("/")))
                sRequest = sRequest + "/"; 
            //Extract the requested file name
            iStartPos = sRequest.LastIndexOf("/") + 1;
            sRequestedFile = sRequest.Substring(iStartPos);

            //Extract The directory Name
            sDirName = sRequest.Substring(sRequest.IndexOf("/"), 

The code is self-explanatory. It receives the request, converts it into string from bytes then looks for the request type, extracts the HTTP Version, file and directory information.

// Identify the Physical Directory
if ( sDirName == "/")
    sLocalDir = sMyWebServerRoot;
    //Get the Virtual Directory
    sLocalDir = GetLocalPath(sMyWebServerRoot, sDirName);

Console.WriteLine("Directory Requested : " +  sLocalDir);

//If the physical directory does not exists then
// dispaly the error message
if (sLocalDir.Length == 0 )
    sErrorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>";
    //sErrorMessage = sErrorMessage + "Please check data\\Vdirs.Dat";

    //Format The Message
    SendHeader(sHttpVersion,  "", sErrorMessage.Length, 
               " 404 Not Found", ref mySocket);

    //Send to the browser
    SendToBrowser(sErrorMessage, ref mySocket);



Note: Microsoft Internet Explorer usually displays a 'friendly' HTTP Error Page if you want to display our error message then you need to disable the 'Show friendly HTTP error messages' option under the 'Advanced' tab in Tools->Internet Options. Next we look if the directory name is supplied, we call GetLocalPath function to get the physical directory information, if the directory not found (or does not mapped with entry in Vdir.Dat) error message is sent to the browser.. Next we will identify the file name, if the filename is not supplied by the user we will call the GetTheDefaultFileName function to retrieve the filename, if error occurred it is thrown to browser.

// Identify the File Name

//If The file name is not supplied then look in 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);




Then we need to identify the MIME type

// Get TheMime Type

String sMimeType = GetMimeType(sRequestedFile);

//Build the physical path
sPhysicalFilePath = sLocalDir + sRequestedFile;
Console.WriteLine("File Requested : " +  sPhysicalFilePath);

Now the final steps of opening the requested file and sending 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);

    int iTotBytes=0;

    sResponse ="";

    FileStream fs = new FileStream(sPhysicalFilePath, 
                    FileMode.Open, FileAccess.Read,
    // Create a reader that can read bytes from the FileStream.

    BinaryReader reader = new BinaryReader(fs);
    byte[] bytes = new byte[fs.Length];
    int read;
    while((read = reader.Read(bytes, 0, bytes.Length)) != 0) 
        // Read from the file and write the data to the network
        sResponse = sResponse + Encoding.ASCII.GetString(bytes,0,read);

        iTotBytes = iTotBytes + read;


    SendHeader(sHttpVersion,  sMimeType, iTotBytes, " 200 OK", ref mySocket);
    SendToBrowser(bytes, ref mySocket);
    //mySocket.Send(bytes, bytes.Length,0);


Compilation and Execution

To compile the program from the command line: 


In my version of.NET I don't need to specify any library name, may be for old versions we need to add the reference to the  dll, using /r parameter.

To run the application simply type the application name and press Enter.


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


The user can also request the Image file.. 


Possible Improvements

There are many improvements can be made to the WebServer application. Currently it does not supports embedded images and has no supports for scripting. We can write our own ISAPI filter for the same or we can also use the IIS ISAPI filter for our learning purpose. The code to write basic ISAPI filters is very well explained at ISAPI Filters: Designing SiteSentry, an Anti-Scraping Filter for IIS


This article gives very basic idea of writing Web server application, lots of improvement can be done. I'll appreciate if I can get any comments on improving the same. I am also looking forward for adding the capabilities of calling Microsoft ISAPI Filter from this application.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Imtiaz Alam
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionNice Article - great job! Pin
Member 168916415-Oct-15 21:28
memberMember 168916415-Oct-15 21:28 
QuestionCan't load images from folders Pin
Member 1123401627-Nov-14 3:07
memberMember 1123401627-Nov-14 3:07 
QuestionJavascript enabled? Pin
Member 108507579-Sep-14 6:55
memberMember 108507579-Sep-14 6:55 
QuestionAn existing connection was forcibly closed by the remote host Pin
kung12323-May-14 0:15
memberkung12323-May-14 0:15 
Questionadding zip files for speed up webserver Pin
Member 903762611-Oct-13 22:49
memberMember 903762611-Oct-13 22:49 
AnswerRe: adding zip files for speed up webserver Pin
Hamed_gibago214-Dec-13 20:02
memberHamed_gibago214-Dec-13 20:02 
Questionworking with css style sheets (cant load them) Pin
Member 90376267-Sep-13 20:07
memberMember 90376267-Sep-13 20:07 
Questionsocket exception error when testing server response Pin
Member 903762621-Aug-13 1:09
memberMember 903762621-Aug-13 1:09 
GeneralMy vote of 5 Pin
david aldlay11-Jan-13 3:40
memberdavid aldlay11-Jan-13 3:40 
Conraddewet12-Sep-12 21:57
memberConraddewet12-Sep-12 21:57 
QuestionAny way we can make it a secured "https" Pin
Deepak Jena7-Sep-12 10:28
memberDeepak Jena7-Sep-12 10:28 
AnswerRe: Any way we can make it a secured "https" Pin
devvvy6-Apr-13 1:57
memberdevvvy6-Apr-13 1:57 
GeneralMy vote of 5 Pin
manoj kumar choubey5-Feb-12 20:10
membermanoj kumar choubey5-Feb-12 20:10 
QuestionInformation related with this webserver Pin
sumit040510-Jan-12 5:09
membersumit040510-Jan-12 5:09 
Questionhttp post Pin
Member 84408188-Jan-12 4:54
memberMember 84408188-Jan-12 4:54 
GeneralMy vote of 5 Pin
hazekaizer7-Jan-12 4:02
memberhazekaizer7-Jan-12 4:02 
QuestionGreat Article, Is it possile to create your own Server-Side Language? Pin
ArikLekar29-Sep-11 0:18
memberArikLekar29-Sep-11 0:18 
AnswerRe: Great Article, Is it possile to create your own Server-Side Language? Pin
Member 84408188-Jan-12 4:49
memberMember 84408188-Jan-12 4:49 
GeneralRe: Great Article, Is it possile to create your own Server-Side Language? Pin
Mohammed Owais25-May-13 22:01
memberMohammed Owais25-May-13 22:01 
GeneralVery nice explanation Pin
paido2-Sep-11 3:55
memberpaido2-Sep-11 3:55 
GeneralMy vote of 5 Pin
gemese24-Jun-11 23:36
membergemese24-Jun-11 23:36 
GeneralWebserver in C# Pin
sachinkarche20-Jan-11 7:30
membersachinkarche20-Jan-11 7:30 
GeneralCreate your own Web Server using C# Pin
Bhim Prakash Singh6-Oct-10 22:25
memberBhim Prakash Singh6-Oct-10 22:25 
GeneralCannot get xsl style sheet - href to external xsl file Pin
Guy Olivier de Saint Albin30-Sep-09 4:55
memberGuy Olivier de Saint Albin30-Sep-09 4:55 
Questioncopyright Pin
wolsabang25-Jan-09 4:24
memberwolsabang25-Jan-09 4:24 
AnswerRe: copyright Pin
jjjkkk1-Feb-09 16:31
memberjjjkkk1-Feb-09 16:31 
GeneralAspx page Pin
kkrisjoy12-Dec-08 4:22
memberkkrisjoy12-Dec-08 4:22 
QuestionCan we use Your web server code to Upload a file from client to Server Pin
amit801225-Jul-08 23:25
memberamit801225-Jul-08 23:25 
AnswerRe: Can we use Your web server code to Upload a file from client to Server Pin
Jim Weiler31-Jul-08 15:14
memberJim Weiler31-Jul-08 15:14 
AnswerRe: Can we use Your web server code to Upload a file from client to Server Pin
Hamed_gibago214-Dec-13 20:10
memberHamed_gibago214-Dec-13 20:10 
GeneralNice intro! Pin
idrivefastlane8-Jun-08 12:07
memberidrivefastlane8-Jun-08 12:07 
GeneralHTTP link Pin
Yellow_Yackets23-Sep-07 17:06
memberYellow_Yackets23-Sep-07 17:06 
GeneralGreat Article Pin
vikas maan19-Sep-07 1:42
membervikas maan19-Sep-07 1:42 
Generalerrors.... Pin
Steven Burns29-Jan-07 11:06
memberSteven Burns29-Jan-07 11:06 
Generalmore errors.... Pin
Steven Burns29-Jan-07 14:56
memberSteven Burns29-Jan-07 14:56 
AnswerEasy fix for FileStream Pin
Reuben200521-Jul-08 23:12
memberReuben200521-Jul-08 23:12 
Generalref buffer and multipart upload file with another task Pin
Hamed_gibago28-Feb-14 22:51
memberHamed_gibago28-Feb-14 22:51 
QuestionLarge Message Headers Pin
Eric Marthinsen2-Mar-06 12:17
memberEric Marthinsen2-Mar-06 12:17 
GeneralExtending the Current Project Pin
Sathya Prakash Dhanabal25-Oct-05 3:00
sussSathya Prakash Dhanabal25-Oct-05 3:00 
GeneralDidn't work Pin
cmxflash18-Jun-05 18:07
membercmxflash18-Jun-05 18:07 
QuestionHow can I communicate with ISAPI or CGI in .NET webserver in VB.NET Pin
shzegi24-Apr-05 18:01
membershzegi24-Apr-05 18:01 
GeneralJust what in needed!!! Pin
Tim Cools2-Feb-04 22:28
memberTim Cools2-Feb-04 22:28 
Generalgreat demo.....but Pin
andrew johnson20-Nov-03 15:37
sussandrew johnson20-Nov-03 15:37 
QuestionConfig files not in XML? Pin
dog_spawn9-Oct-03 6:33
memberdog_spawn9-Oct-03 6:33 
GeneralHTTP POST Pin
dthartman29-Aug-03 22:14
memberdthartman29-Aug-03 22:14 
GeneralRe: HTTP POST Pin
lubosh21-Sep-03 2:07
susslubosh21-Sep-03 2:07 
GeneralRe: HTTP POST Pin
Member 12771891-Feb-09 23:35
memberMember 12771891-Feb-09 23:35 
GeneralRe: HTTP POST Pin
Member 12771891-Feb-09 23:38
memberMember 12771891-Feb-09 23:38 
GeneralRe: HTTP POST Pin
jonataspc19-Oct-09 11:18
memberjonataspc19-Oct-09 11:18 
GeneralRe: HTTP POST Pin
jonataspc19-Oct-09 11:44
memberjonataspc19-Oct-09 11:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.151126.1 | Last Updated 28 Oct 2001
Article Copyright 2001 by Imtiaz Alam
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid