Recipes
In this chapter, the structure of HTTP requests and responses was examined. As shown in Listing 1.3 and 1.4, the structure of HTTP is not terribly complex. As a result, it is relatively easy to create a web server. This is what the two recipes in this chapter will deal with. The first Recipe, 1.1, will show how to create a really simple web server. Next, Recipe 1.2 will show how to extend the simple web server to use HTML and image files, just as a regular web server would.
The recipes in this chapter make use of C# sockets. Sockets are the lowest level that an application programmer will usually get to the Internet connection. The socket level allows a web server to be created. After this chapter, all C# recipes will make use of the C# HTTP classes. If desired, HTTP programming could be performed at the socket level; however, using the C# HTTP classes will get the needed functionality, without the complexity of dealing directly with sockets.
Recipe 1.1: A Simple Web Server
The first recipe is a “Hello World” program of sorts. Recipe 1.1 is a web server. Its purpose is to show how to serve web pages from your program. This very simple program only serves one web page. This page simply says “Hello World”.
When this program is launched the port that web server will listen at must be specified. Normally web servers listen at port 80. However, there may already be a web server running at port 80. If this is the case a higher port number such as 8080 or 8081 should be used. Typing the following command in a command prompt window will start the web server.
Recipe1_1 8080
This will start the web server on port 8080. If something already has that port in use then a BindingException error message will be shown, as seen in Listing 1.5. For more information on how to execute the recipes in this book refer to Appendix B, “Compiling and Executing Examples.”
Listing 1.5: Port Already in Use
Unhandled Exception: System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddresssocketAddress) at System.Net.Sockets.Socket.Bind(EndPoint localEP) at HeatonResearch.httprecipes.ch1.Recipe1_1.SimpleWebServer..ctor(Int32 port) in C:\Documents and Settings\jeff\My Documents\Visual Studio 2005\Projects\HTTPRecipes\Recipe1_1\SimpleWebServer.cs:line 42 at HeatonResearch.httprecipes.ch1.Recipe1_1.SimpleWebServer.Main(String[] args) in C:\Documents and Settings\jeff\My Documents\Visual Studio 2005\Projects\HTTPRecipes\Recipe1_1\SimpleWebServer.cs:line 137
If the web server is started properly, and no error occurs, there should be no output. The web server is now waiting for a connection. Connecting to the web server is easy. Use any web browser and access the following URL:
No matter what request is sent to this web server, it will produce a page that says Hello World. The output from Recipe 1.1 is shown in Figure 1.7.
Figure 1.7: Hello World

Now that the program has been demonstrated, it is time to take a look at what was necessary to implement this program. Recipe 1.1 is shown in Listing 1.6.
Listing 1.6: Simple Web Server (SimpleWebServer.cs)
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
namespace HeatonResearch.httprecipes.ch1.Recipe1_1
{
/// <summary>
/// Recipe #1.1: Very Simple Web Server
/// Copyright 2007 by Jeff Heaton(jeff@jeffheaton.com)
///
/// HTTP Programming Recipes for C# Bots
/// ISBN: 0-9773206-7-7
/// http://www.heatonresearch.com/articles/series/20/
///
/// A simple web server that will respond to every request
/// with "Hello World".
///
/// This software is copyrighted. You may use it in programs
/// of your own, without restriction, but you may not
/// publish the source code without the author's permission.
/// For more information on distributing this code, please
/// visit:
/// http://www.heatonresearch.com/hr_legal.php
/// </summary>
class SimpleWebServer
{
/// <summary>
/// The server socket that will listen for connections
/// </summary>
private Socket server;
/// <summary>
/// Construct the web server to listen on the specified
/// port.
/// </summary>
/// <param name="port">The port to use for the server.</param>
public SimpleWebServer(int port)
{
server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP);
server.Bind(new IPEndPoint(IPAddress.Loopback,port));
}
/// <summary>
/// The run method endlessly waits for connections
/// as each connection is opened(from web browsers)
/// the connection is passed off to handleClientSession.
/// </summary>
public void run()
{
server.Listen(10);
for(;;)
{
Socket socket = server.Accept();
HandleClientSession(socket);
}
}
/// <summary>
/// Read a string from the socket.
/// </summary>
/// <param name="socket">The socket to read from.</param>
/// <returns>THe string read.</returns>
private String SocketRead(Socket socket)
{
StringBuilder result = new StringBuilder();
byte []buffer = new byte[1];
while( socket.Receive(buffer)>0 )
{
char ch = (char)buffer[0];
if( ch=='\n')
break;
if( ch!='\r')
result.Append(ch);
}
return result.ToString();
}
/// <summary>
/// Write a string to the socket, followed by a line break.
/// </summary>
/// <param name="socket">The socket to write to.</param>
/// <param name="str">What to write to the socket.</param>
private void SocketWrite(Socket socket,String str)
{
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
socket.Send(encoding.GetBytes(str) );
socket.Send(encoding.GetBytes("\r\n"));
}
/// <summary>
/// Handle a client session. This method displays the incoming
/// HTTP request and responds with a "Hello World" response.
/// </summary>
/// <param name="socket">The client socket.</param>
private void HandleClientSession(Socket socket)
{
// read in the first line
Console.WriteLine("**New Request**");
String first = SocketRead(socket);
Console.WriteLine(first);
// read in headers and post data
String line;
do
{
line = SocketRead(socket);
if(line!=null)
Console.WriteLine(line);
} while (line!=null && line.Length>0 );
// write the HTTP response
SocketWrite(socket,"HTTP/1.1 200 OK");
SocketWrite(socket,"");
SocketWrite(socket,"<html>");
SocketWrite(socket,"<head><title>Simple Web Server</title></head>");
SocketWrite(socket,"<body>");
SocketWrite(socket,"<h1>Hello World</h1>");
SocketWrite(socket,"<//body>");
SocketWrite(socket,"</html>");
// close everything up
socket.Close();
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
SimpleWebServer webServer = new SimpleWebServer(80);
webServer.run();
}
}
}
C# supports two primary types of sockets: server sockets and client sockets. Both types are implemented through the Socket class. A socket becomes a server socket when the Bind method is called to bind the server socket to a specific port. This program begins by creating a server socket and binding it to the specified port. This is done with the following line of code in the WebServer constructor:
server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP); server.Bind(new IPEndPoint(IPAddress.Loopback,port));
Once the server connection has been opened, the program must wait for connections. This is done using the Accept function of the Socket object. This is done in the Run method. The Run method begins by entering an endless loop, as seen here:
for (;;)
{This command causes the web server to wait endlessly for connections. The server does not include a mechanism for shutting itself down. To shut down the server simply, press ctrl-c or close the web server’s window.
Next, the Accept function is called to accept a connection. If no connection is available, then the Accept function blocks (or waits), until a connection is available. Because of this, the Accept call is often put inside of a thread. This would allow the rest of the program to continue executing while the thread waits for a connection. However, for this simple example, everything will be done in a single thread.
Socket socket = server.Accept();
When a connection is made, a Socket object is returned. This object is passed onto the HandleClientSession method to fulfill the request.
HandleClientSession(socket);
The HandleClientSession method will handle all input and output to the socket with the functions ReadSocket and WriteSocket. These functions will be discussed later in this section. As was discussed earlier in the chapter, the first line of an HTTP request has a special format. Because of this, the first line is read separately. It is then printed out.
// read in the first line
Console.WriteLine("**New Request**");
String first = SocketRead(socket);
Console.WriteLine(first);Once the first line has been read in, the headers can be read. The headers are read until a blank line is found. As was discussed earlier in this chapter, a blank line indicates the end of HTTP headers.
// read in headers and post data
String line;
do
{
line = SocketRead(socket);
if(line!=null)
Console.WriteLine(line);
} while (line!=null && line.Length>0 );Once the first blank line is hit, the program is done reading the HTTP headers. Because this server only supports GET requests, the program is also done reading the HTTP request. Now it is time to write the HTTP response. The following lines of code write a simple HTML message that says “Hello World” to the browser.
// write the HTTP response SocketWrite(socket,"HTTP/1.1 200 OK"); SocketWrite(socket,""); SocketWrite(socket,"<html>"); SocketWrite(socket,"<head><title>Simple Web Server</title></head>"); SocketWrite(socket,"<body>"); SocketWrite(socket,"<h1>Hello World</h1>"); SocketWrite(socket,"<//body>"); SocketWrite(socket,"</html>");
Now that the message has been written, it is time to close the streams. The following lines of code do this.
// close everything up socket.Close();
The above recipe showed how to create a simple web server. In addition to this basic functionality, this recipe can be expanded to be a custom web server that will respond to different requests. For example, a web server could be constructed to give information about how a process is running or other status information collected by the computer.
Most web servers simply present files to the browser. However, Recipe 1.2 generated its response internally. This can be a very useful technique to display status information for your web server. The next recipe, Recipe 1.2, shows how to create a web server that will allow access to files.
Reading from the Socket
The ReadSocket function will read a single line of text from the socket. This line is delimited by a new-line character (\n). The ReadSocket function can be called to read each of the HTTP headers sent by the web browser.
To read from the socket use the Receive function. The Receive function will attempt to fill a buffer with data read from the socket. For the simple web server we will read the data one character at a time. If the character is a new-line character (\n) then the reading stops. If the character is not a carriage return, then it is appended to the current line.
StringBuilder result = new StringBuilder();
byte []buffer = new byte[1];
while( socket.Receive(buffer)>0 )
{
char ch = (char)buffer[0];
if( ch=='\n')
break;
if( ch!='\r')
result.Append(ch);
}
return result.ToString();Finally, the line that was built up in the result variable is returned as a String.
Writing to the Socket
To write a line of text to the socket the WriteLine method is used. This method uses the ASCIIEncoding class to properly encode the ASCII line into a binary form that can be transmitted with the socket.
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
socket.Send(encoding.GetBytes(str) );
socket.Send(encoding.GetBytes("\r\n"));The specified line of text is written. After the line is written, the end of line carriage return and linefeed (\r\n) are written.
Recipe 1.2: File Based Web Server
This recipe shows how to create a very common sort of web server. This web sever exposes a directory tree to the Internet. This directory tree is called the “HTTP root”, or “web root”. Files are placed into this directory to be accessed by web browsers. A default file, named index.html, should be placed into this directory. This file is displayed when the user browses to the directory. The index.html file usually has links to the other files in that directory, and serves as a starting point.
This web server requires only two configuration arguments. Both of these are specified in the command line. The two parameters are:
- HTTP Port
- HTTP Root Directory
For example, to start the web server using port 8080 and the directory c:\httproot\ as the root directory, the following command is used.
Recipe1_2 8080 c:\httproot\
The web server featured in this recipe is an expanded version of the server featured in Recipe 1.1. Because of this the details will not be repeated that are the same between the two web servers; therefore, Recipe 1.1 should be reviewed for additional information. For more information on how to execute the recipes in this book refer to Appendix B, “Compiling and Executing Examples.”
Now the construction of the web server will be examined. Listing 1.7 shows the source code necessary for the file based web server.
Listing 1.7: File Based Web Server (WebServer.cs)
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.IO;
namespace HeatonResearch.httprecipes.ch1.Recipe1_2
{
/// <summary>
/// Recipe #1.2: Simple File Based Web Server
/// Copyright 2007 by Jeff Heaton(jeff@jeffheaton.com)
///
/// HTTP Programming Recipes for C# Bots
/// ISBN: 0-9773206-7-7
/// http://www.heatonresearch.com/articles/series/20/
///
/// A simple web server that exposes a single directory as the
/// root of a web site. Only the most basic web server functions
/// are provided, such as index.html redirect.
///
/// This software is copyrighted. You may use it in programs
/// of your own, without restriction, but you may not
/// publish the source code without the author's permission.
/// For more information on distributing this code, please
/// visit:
/// http://www.heatonresearch.com/hr_legal.php
///
/// @author Jeff Heaton
/// @version 1.1
/// </summary>
class WebServer
{
/// <summary>
/// The server socket that will listen for connections
/// </summary>
private Socket server;
/// <summary>
/// Used to convert strings to byte arrays.
/// </summary>
private System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
/// <summary>
/// Where the HTML files are stored.
/// </summary>
private String httproot;
/// <summary>
/// Construct the web server to listen on the specified
/// port.
/// </summary>
/// <param name="port">The port to use for the server.</param>
public WebServer(int port,String httproot)
{
server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP);
server.Bind(new IPEndPoint(IPAddress.Loopback,80));
this.httproot = httproot;
}
/// <summary>
/// The run method endlessly waits for connections
/// as each connection is opened(from web browsers)
/// the connection is passed off to handleClientSession.
/// </summary>
public void Run()
{
server.Listen(10);
for(;;)
{
Socket socket = server.Accept();
HandleClientSession(socket);
}
}
/// <summary>
/// Read a string from the socket.
/// </summary>
/// <param name="socket">The socket to read from.</param>
/// <returns>THe string read.</returns>
private String SocketRead(Socket socket)
{
StringBuilder result = new StringBuilder();
byte []buffer = new byte[1];
while( socket.Receive(buffer)>0 )
{
char ch = (char)buffer[0];
if( ch=='\n')
break;
if( ch!='\r')
result.Append(ch);
}
return result.ToString();
}
/// <summary>
/// Write a string to the socket, followed by a line break.
/// </summary>
/// <param name="socket">The socket to write to.</param>
/// <param name="str">What to write to the socket.</param>
private void SocketWrite(Socket socket,String str)
{
socket.Send(encoding.GetBytes(str) );
socket.Send(encoding.GetBytes("\r\n"));
}
/// <summary>
/// Handle a client session. This method displays the incoming
/// HTTP request and responds with a "Hello World" response.
/// </summary>
/// <param name="socket">The client socket.</param>
private void HandleClientSession(Socket socket)
{
// read in the first line
Console.WriteLine("**New Request**");
String first = SocketRead(socket);
Console.WriteLine(first);
// read in headers and post data
String line;
do
{
line = SocketRead(socket);
if(line!=null)
Console.WriteLine(line);
} while (line!=null && line.Length>0 );
// write the HTTP response
char []delim = {' '};
String []tok = first.Split();
String verb = tok[0];
String path = tok[1];
String version = tok[2];
if ( String.Compare(verb,"GET",true)==0 )
SendFile(socket, path);
else
Error(socket, 500, "Unsupported command");
// close everything up
socket.Close();
}
/// <summary>
/// Add a slash to the end of a path, if there is not a slash
/// there already. This method adds the correct type of slash,
/// depending on the operating system.
/// </summary>
/// <param name="path">The path to add a slash to.</param>
private void AddSlash(StringBuilder path)
{
if (!path.ToString().EndsWith("\\"))
path.Append("\\");
}
/// <summary>
/// Determine the correct "content type" based on the file
/// extension.
/// </summary>
/// <param name="path">The file being transfered.</param>
/// <returns>The correct content type for this file.</returns>
private String GetContent(String path)
{
path = path.ToLower ();
if (path.EndsWith(".jpg") || path.EndsWith(".jpeg"))
return "image/jpeg";
else if (path.EndsWith(".gif"))
return "image/gif";
else if (path.EndsWith(".png"))
return "image/png";
else
return "text/html";
}
/// <summary>
/// Transmit a HTTP response. All responses are handled by
/// this method.
/// </summary>
/// <param name="socket">The socket to transmit to.</param>
/// <param name="code">The response code, i.e. 404 for not found.</param>
/// <param name="message">The message, usually OK or error message.</param>
/// <param name="body">The data to be transfered.</param>
/// <param name="content">The content type.</param>
private void Transmit(
Socket socket,
int code,
String message,
byte []body,
String content)
{
StringBuilder headers = new StringBuilder();
headers.Append("HTTP/1.1 ");
headers.Append(code);
headers.Append(' ');
headers.Append(message);
headers.Append("\n");
headers.Append("Content-Length: " + body.Length + "\n");
headers.Append("Server: Heaton Research Example Server\n");
headers.Append("Connection: close\n");
headers.Append("Content-Type: " + content + "\n");
headers.Append("\n");
socket.Send(encoding.GetBytes(headers.ToString()));
socket.Send(body);
}
/// <summary>
/// Display an error to the web browser.
/// </summary>
/// <param name="socket">The socket to display the error to.</param>
/// <param name="code">The response code, i.e. 404 for not found.</param>
/// <param name="message">The error that occured.</param>
private void Error(Socket socket, int code, String message)
{
StringBuilder body = new StringBuilder();
body.Append("<html><head><title>");
body.Append(code + ":" + message);
body.Append("</title></head><body><p>An error occurred.</p><h1>");
body.Append(code);
body.Append("</h1><p>");
body.Append(message);
body.Append("</p></body></html>");
Transmit(socket, code, message, encoding.GetBytes(body.ToString()), "text/html");
}
/// <summary>
/// Send a disk file. The path passed in is from the URL, this
/// URL is translated into a local disk file, which is then
/// transfered.
/// </summary>
/// <param name="socket">The socket to send to.</param>
/// <param name="path">The file requested from the URL.</param>
private void SendFile(Socket socket, String path)
{
char []delim = { '/' };
// parse the file by /'s and build a local file
String []tok = path.Split(delim);
Console.WriteLine(path);
StringBuilder physicalPath = new StringBuilder(httproot);
AddSlash(physicalPath);
foreach(String e in tok)
{
if (!e.Trim().Equals("\\") )
{
if (e.Equals("..") || e.Equals("."))
{
Error(socket, 500, "Invalid request");
return;
}
AddSlash(physicalPath);
physicalPath.Append(e);
}
}
// if there is no file specified, default
// to index.html
if (physicalPath.ToString().EndsWith("\\"))
{
physicalPath.Append("index.html");
}
String filename = physicalPath.ToString();
// open the file and send it if it exists
FileInfo file = new FileInfo(filename);
if (file.Exists)
{
// send the file
FileStream fis = File.Open(filename,FileMode.Open);
byte []buffer = new byte[(int) file.Length];
fis.Read(buffer,0,buffer.Length);
fis.Close();
this.Transmit(socket, 200, "OK", buffer, GetContent(filename));
}
// file does not exist, so send file not found
else
{
this.Error(socket, 404, "File Not Found");
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Usage:\nRecipe1_2 [port] [http root path]");
}
else
{
int port;
try
{
port = int.Parse(args[0]);
WebServer server = new WebServer(port, args[1]);
server.Run();
}
catch (ArgumentNullException)
{
Console.WriteLine("Invalid port number");
}
catch(FormatException)
{
Console.WriteLine("Invalid port number");
}
catch(OverflowException)
{
Console.WriteLine("Invalid port number");
}
}
}
}
}
The Main function, the constructor, and the Run method are all nearly the same as those in Recipe 1.1. The only difference is the support of the additional command line argument for the http root path. For more information on these three methods, review Recipe 1.1.
The HandleClientSession method begins the same as Recipe 1.1; however, once the connection is established, this recipe becomes more complex.
// write the HTTP response
char []delim = {' '};
String []tok = first.Split();
String verb = tok[0];
String path = tok[1];
String version = tok[2];
if ( String.Compare(verb,"GET",true)==0 )
SendFile(socket, path);
else
Error(socket, 500, "Unsupported command");
// Write the HTTP response.
StringTokenizer tok = new StringTokenizer(first);
String verb = (String) tok.nextElement();
String path = (String) tok.nextElement();
String version = (String) tok.nextElement();As can be seen above, the first line of the HTTP request is parsed. The first line of a HTTP request will be something like the following form.
GET /index.html HTTP/1.1
As previously discussed in this chapter, there are three parts of this line, separated by spaces. Using the Split function the string can be broken into the three parts. The verb is checked to see if it is a request other than GET. If the request is not a GET request, then an error is displayed. Otherwise, the path is sent onto the SendFile method.
The next few sections will discuss the major methods provided in this recipe.
The Send File Method
The SendFile method is used to send a file to the web browser. This consists of a two step process:
- Figure out the local path to the file
- Read in and transmit the file
An HTTP request will come in requesting a file path such as /images/logo.gif. This must be translated to a local path such as c:\httproot\images\logo.gif. This transformation is the first thing that the SendFile method does.
First the Split function is used to break up the path using slashes (/) as delimiters. This is done using the following lines of code.
// parse the file by /'s and build a local file String []tok = path.Split(delim); Console.WriteLine(path); StringBuilder physicalPath = new StringBuilder(httproot); AddSlash(physicalPath);
The physicalPath variable will hold the path to the file to be transferred. A slash is added, using the AddSlash function. The physicalPath is now ready to have subdirectories or files concatenated to it. The SendFile method will then parse the HTTP path and concatenate any sub directories followed by the file requested to the physicalPath variable. The following lines of code begin this loop:
foreach(String e in tok)
{
if (!e.Trim().Equals("\\") )
{As the elements of the file are parsed, the program must look out for the previous directory code of “..”. If “..” is allowed to be part of the path, a malicious user could use “..” to access the parent HTTP root directory. This would be a security risk. Therefore, if the string “..” is located inside of the URL, an error is displayed.
if (e.Equals("..") || e.Equals("."))
{
Error(socket, 500, "Invalid request");
return;
}For each section, the sub directory, or file, is concatenated to the physicalPath variable. Additionally, a slash is added for each of the sub directory levels.
AddSlash(physicalPath); physicalPath.Append(e);
Now, that the entire path has been parsed, it is time to check for a default file. If the path specified by the user is a directory only, the default file index.html needs to be specified as shown below:
// if there is no file specified, default
// to index.html
if (physicalPath.ToString().EndsWith("\\"))
{
physicalPath.Append("index.html");
}
String filename = physicalPath.ToString();Once the path is complete, there are really only two possibilities that will occur. Either the file will be transmitted to the user or a 404 error will be generated. The error code 404, which is the most famous of HTTP error codes, means that the file was not found.
Next the file that is to be transmitted must be read. The following lines of code will read the file.
// open the file and send it if it exists
FileInfo file = new FileInfo(filename);
if (file.Exists)
{
// send the file
FileStream fis = File.Open(filename,FileMode.Open);
byte []buffer = new byte[(int) file.Length];
fis.Read(buffer,0,buffer.Length);
fis.Close();
this.Transmit(socket, 200, "OK", buffer, GetContent(filename));
}As can be seen from the above lines of code, the file is read into an array of bytes. Once the file has been read, the Transmit method is called. The Transmit method actually transmits the data to the web browser.
If the file can not be found, an error is sent to the web browser.
// file does not exist, so send file not found
else
{
this.Error(socket, 404, "File Not Found");
}Notice the last parameter sent to the Transmit method. It is the content type. This tells the web browser what type of data the file contains. The next section explains how this is determined.
The Get Content Function
Since the Transmit method needs to know what type of data is being transferred, the GetContent function should be called to determine the content type. The content type will be a string such as image/gif for a GIF image or text/html for an HTML file. This type is determined by the file extension, as shown in the following lines of code:
path = path.ToLower ();
if (path.EndsWith(".jpg") || path.EndsWith(".jpeg"))
return "image/jpeg";
else if (path.EndsWith(".gif"))
return "image/gif";
else if (path.EndsWith(".png"))
return "image/png";
else
return "text/html";The GetContent function can be called to quickly determine the content type based on the filename. Content types themselves will be discussed in greater detail in Chapter 4, “Using the HTTP Classes”.
The Error Method
When an error occurs, the Error method is called. The Error method accepts three arguments:
- The output stream
- The error code
- The error message
The Error method works by constructing an HTML page that displays the error. This code can be seen here:
StringBuilder body = new StringBuilder();
body.Append("<html><head><title>");
body.Append(code + ":" + message);
body.Append("</title></head><body><p>An error occured.</p><h1>");
body.Append(code);
body.Append("</h1><p>");
body.Append(message);
body.Append("</p></body></html>");This HTML page is then converted into an array of bytes. Next, this array of bytes, along with the code and message, is passed to the Transmit method. Finally, the Transmit method will send this data to the web browser.
Transmit(socket, code, message, encoding.GetBytes(body.ToString()), "text/html");
The Error method is handy because it can be called from several different locations when an error occurs.
The Transmit Method
Both the Error and SendFile methods use the Transmit method to actually send the page to the web browser. This is very convenient because the Transmit method properly handles all of the HTTP headers, and thus saves both the Error and SendFile methods from both having to implement this functionality.
First, the HTTP headers are constructed. The HTTP headers are constructed into a StringBuilder, as seen here:
StringBuilder headers = new StringBuilder();
headers.Append("HTTP/1.1 ");
headers.Append(code);
headers.Append(' ');
headers.Append(message);
headers.Append("\n");
headers.Append("Content-Length: " + body.Length + "\n");
headers.Append("Server: Heaton Research Example Server\n");
headers.Append("Connection: close\n");
headers.Append("Content-Type: " + content + "\n");
headers.Append("\n");Once the headers have been constructed, both the header and body can be transmitted. This is done using the following two commands.
socket.Send(encoding.GetBytes(headers.ToString())); socket.Send(body);
As can be seen, Recipe 1.2 implements a very simple, yet functional, web server. This web server is far from being “industrial strength”, but it would serve as a great starting point for any sort of application that would require a built-in web server.




