下载源代码

介绍

我们将学习如何写一个简单的web服务器,用于响应知名的HTTP请求(GET和POST),用C#发送响应。然后,我们从网络访问这台服务器,这次我们会说“Hello world!”

背景

        HTTP协议

HTTP是服务器和客户机之间的通信协议。它使用TCP/IP协议来发送/接收请求/响应。

有几个HTTP方法,我们将实现两个:GET和POST。

GET

当我们将一个地址输入到我们的Web浏览器的地址栏中,按下回车键时,会发生什么情况?(虽然我们使用TCP/IP,但我们不指定端口号,因为HTTP默认使用80端口,我们并不需要指定80)

GET / HTTP/1.1\r\n
Host: okbase.net\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: tr-tr,tr;q=0.8,en-us;q=0.5,en;q=0.3\r\n
Accept-Encoding: gzip, deflate\r\n
Connection: keep-alive\r\n\r\n

该GET请求使用TCP/IP通过浏览器向服务器发送,请求的是okbase.net的根目录下的内容。我们可以添加更多的头信息,最基本的信息如下:

GET / HTTP/1.1\r\n
Host: okbase.net\r\n\r\n

POST

POST请求和GET请求类似,在GET请求里,变量加到url的?下面,POST请求,变量加到两行回车的下面,并需要指定内容长度。

POST /index.html HTTP/1.1\r\n
Host: atasoyweb.net\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: tr-tr,tr;q=0.8,en-us;q=0.5,en;q=0.3\r\n
Accept-Encoding: gzip, deflate\r\n
Connection: keep-alive\r\n
Referer: http://okbase.net/\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 35\r\n\r\n
variable1=value1&variable2=value2

简化版本如下:

POST /index.html HTTP/1.1\r\n
Host: okbase.net\r\n
Content-Length: 35\r\n\r\n
variable1=value1&variable2=value2

 响应

当服务器接收到一个请求进行解析,并返回一个响应状态代码:

HTTP/1.1 200 OK\r\n
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\r\n
Content-Length: {content_length}\r\n
Connection: close\r\n
Content-Type: text/html; charset=UTF-8\r\n\r\n
the content of which length is equal to {content_length}

这是一个响应头,"200 OK"便是一切OK,请求的内容将被返回。状态码有很多,我们经常使用200,501,404。

“501 Not Implemented”方法未实现。我们将只实现GET和POST。如果通过其它方法请求,我们将发送此代码。

“404 Not Found”:没有找到请求的内容。

内容类型

服务器必须指定它们的响应中的内容的类型。有许多内容类型,这些也被称为“MIME(多用途互联网邮件扩展)类型”(因为它们也可以用来识别非ASCII的电子邮件)。以下是在我们的实现中,我们将使用的内容类型:(您可以修改代码并添加更多)

text/html
        text/xml
        text/plain
        text/css
        image/png
        image/gif
        image/jpg
        image/jpeg
        application/zip

如果服务器指定了错误的内容类型的内容会被曲解。例如,如果一台服务器发送纯文本,使用“图像/ png”类型,客户端试图显示的文字图像。

多线程

如果我们使我们的服务器可以同时响应多个客户端,我们必须为每个请求创建新的线程。因此,每个线程处理一个请求,并退出完成它的使命。(多线程也加快了页面加载,因为如果我们请求一个页面,页面中使用了CSS和图像,每个图像和CSS文件会以GET方式发送请求)。

一个简单的Web服务器的实现

现在,我们准备实现一个简单的Web服务器。首先,让我们来定义变量,我们将使用:

public bool running = false; // Is it running?private int timeout = 8; // Time limit for data transfers.
private Encoding charEncoder = Encoding.UTF8; // To encode string
private Socket serverSocket; // Our server socket
private string contentPath; // Root path of our contents// Content types that are supported by our server
// You can add more...
// To see other types: http://www.webmaster-toolkit.com/mime-types.shtml
private Dictionary<string, string> extensions = new Dictionary<string, string>()
{ //{ "extension", "content type" }{ "htm", "text/html" },{ "html", "text/html" },{ "xml", "text/xml" },{ "txt", "text/plain" },{ "css", "text/css" },{ "png", "image/png" },{ "gif", "image/gif" },{ "jpg", "image/jpg" },{ "jpeg", "image/jpeg" },{ "zip", "application/zip"}
};

启动服务器的方法:

public bool start(IPAddress ipAddress, int port, int maxNOfCon, string contentPath)
{if (running) return false; // If it is already running, exit.try{// A tcp/ip socket (ipv4)serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);serverSocket.Bind(new IPEndPoint(ipAddress, port));serverSocket.Listen(maxNOfCon);serverSocket.ReceiveTimeout = timeout;serverSocket.SendTimeout = timeout;running = true;this.contentPath = contentPath;}catch { return false; }// Our thread that will listen connection requests// and create new threads to handle them.Thread requestListenerT = new Thread(() =>{while (running){Socket clientSocket;try{clientSocket = serverSocket.Accept();// Create new thread to handle the request and continue to listen the socket.Thread requestHandler = new Thread(() =>{clientSocket.ReceiveTimeout = timeout;clientSocket.SendTimeout = timeout;try { handleTheRequest(clientSocket); }catch{try { clientSocket.Close(); } catch { }}});requestHandler.Start();}catch{}}});requestListenerT.Start();return true;
}

停止服务器的方法

public void stop()
{if (running){running = false;try { serverSocket.Close(); }catch { }serverSocket = null;}
}

最重要的部分代码

private void handleTheRequest(Socket clientSocket)
{byte[] buffer = new byte[10240]; // 10 kb, just in caseint receivedBCount = clientSocket.Receive(buffer); // Receive the requeststring strReceived = charEncoder.GetString(buffer, 0, receivedBCount);// Parse method of the requeststring httpMethod = strReceived.Substring(0, strReceived.IndexOf(" "));int start = strReceived.IndexOf(httpMethod) + httpMethod.Length + 1;int length = strReceived.LastIndexOf("HTTP") - start - 1;string requestedUrl = strReceived.Substring(start, length);string requestedFile;if (httpMethod.Equals("GET") || httpMethod.Equals("POST"))requestedFile = requestedUrl.Split('?')[0];else // You can implement other methods...{notImplemented(clientSocket);return;}requestedFile = requestedFile.Replace("/", @"\").Replace("\\..", "");start = requestedFile.LastIndexOf('.') + 1;if (start > 0){length = requestedFile.Length - start;string extension = requestedFile.Substring(start, length);if (extensions.ContainsKey(extension)) // Do we support this extension?if (File.Exists(contentPath + requestedFile)) //If yes check existence of the file// Everything is OK, send requested file with correct content type:sendOkResponse(clientSocket,File.ReadAllBytes(contentPath + requestedFile), extensions[extension]);elsenotFound(clientSocket); // We don't support this extension.// We are assuming that it doesn't exist.}else{// If file is not specified try to send index.htm or index.html// You can add more (default.htm, default.html)if (requestedFile.Substring(length - 1, 1) != @"\")requestedFile += @"\";if (File.Exists(contentPath + requestedFile + "index.htm"))sendOkResponse(clientSocket,File.ReadAllBytes(contentPath + requestedFile + "\\index.htm"), "text/html");else if (File.Exists(contentPath + requestedFile + "index.html"))sendOkResponse(clientSocket,File.ReadAllBytes(contentPath + requestedFile + "\\index.html"), "text/html");elsenotFound(clientSocket);}
}

不同的状态代码的响应:

private void notFound(Socket clientSocket)
{sendResponse(clientSocket, "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><h2>Atasoy Simple Web Server</h2><div>404 - Not Found</div></body></html>", "404 Not Found", "text/html");
}private void sendOkResponse(Socket clientSocket, byte[] bContent, string contentType)
{sendResponse(clientSocket, bContent, "200 OK", contentType);
}private void notImplemented(Socket clientSocket)
{sendResponse(clientSocket, "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><h2>Atasoy Simple Web Server</h2><div>501 - Method Not Implemented</div></body></html>", "501 Not Implemented", "text/html");}

将响应发送到客户端的方法

// For strings
private void sendResponse(Socket clientSocket, string strContent, string responseCode,string contentType)
{byte[] bContent = charEncoder.GetBytes(strContent);sendResponse(clientSocket, bContent, responseCode, contentType);
}// For byte arrays
private void sendResponse(Socket clientSocket, byte[] bContent, string responseCode,string contentType)
{try{byte[] bHeader = charEncoder.GetBytes("HTTP/1.1 " + responseCode + "\r\n"+ "Server: Atasoy Simple Web Server\r\n"+ "Content-Length: " + bContent.Length.ToString() + "\r\n"+ "Connection: close\r\n"+ "Content-Type: " + contentType + "\r\n\r\n");clientSocket.Send(bHeader);clientSocket.Send(bContent);clientSocket.Close();}catch { }
}

用法

// to create new one:
Server server = new Server();
// to start it
server.start(ipAddress, port, maxconnections, contentpath);
// to stop it
server.stop();

向全世界说"Hello"

我们简单的Web服务器已准备就绪。现在,我们将从Internet访问它。为了实现这一目标,我们必须将请求从Modem重定向到我们的计算机。如果我们的调制解调器支持UPnP那就很简单。

1. 下载UPnp Port Forwarder ,并运行它。

2. 点击“Search For Devices”按钮。如果您的调制解调器支持UPnP,它会被添加到ComboBox。

3. 点击“Update List”按钮,列出转发端口。

4. 然后点击“Add New”按钮,填写表格。

5. 如果选中“IP”复选框,并输入一个IP地址,只有从这个IP的请求将被重定向。所以,千万不要填写。

6. 内部端口必须等于我们服务器的端口。

7.“ Port”和“ Internal port”可以不相同。

这样,所有来自externalip:port的请求都将通过modem转发到我们的电脑上,你可以用http://www.web-sniffer.net/ 来测试连接的有效性,输入外部IP和端口号 http://外部IP:端口号并点击Submit按钮...

http://blog.okbase.net/haobao/archive/60.html

C#开发自己的Web服务器相关推荐

  1. 自主开发的小型Web服务器

    自主开发的小型Web服务器 1. 技术特点 2. 具体步骤 3. CGI技术 4. Mysql连接 5. Gitee原码链接 6. 参考Blog 1. 技术特点 网络编程(http协议,TCP/IP协 ...

  2. 迅为IMX6ULL开发板搭建Web服务器(二)

    boa 拷贝到开发板的 bin 目录下 接下来在开发板根目录下建立 www 文件夹,如图 80.15. 然后在开发板上面建立的 www 目录下面建立文件夹 cgi-bin 目录,如图 80.16: 然 ...

  3. arduino 网页服务器,如何将Arduino开发板用作Web服务器

    通过使用以太网扩展板(Ethernet shield),您可以将Arduino开发板用作一个Web服务器. 通过向Arduino开发板配备一个以太网扩展板,您可以将其变成简易的Web服务器,并通过在与 ...

  4. [golang Web开发] 1.golang web开发:简介以及web服务器的搭建以及http协议简介

    一.web开发简介 1.简介 Web应用在我们的生活中无处不在,看看我们日常使用的各个应用程序,它们要么是 Web应用,要么是移动 App 这类 web应用的变种,无论哪一种编程语言,只要它能够开发出 ...

  5. Java Web基础入门第八讲 Java Web开发入门——初始WEB服务器

    WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源.Internet上供外界访问的Web资源分为: 静态web资源(如html页面):指we ...

  6. android与mysql数据库同步_android开发 如何通过web服务器访问MYSQL数据库并且使其数据同步到android SQLite数据库?...

    通过web服务器访问MYSQL数据库有以下几个过程: 1.在MySql下创建自己的数据库和自己的表单 2.连接数据库. 3.访问数据库 1.创建web工程 (服务器端) 在Myeclipse下新建一个 ...

  7. go语言服务器代码,Go语言开发简单web服务器

    欢迎,来自IP地址为:182.103.254.107 的朋友 Go语言由于其方便的并发通信机制以及强大的网络支持,常常被用于服务器软件的开发.本文将示例使用Go语言来开发简单的Web服务器. HTTP ...

  8. IIS Web 服务器/ASP.NET 运行原理基本知识概念整理

    前言: 记录 IIS 相关的笔记还是从公司笔试考核题开始的,问 Application Pool 与 AppDomain 的区别? 促使我对进程池进了知识的学习,所以记录一下学习的笔记. 我们知道现在 ...

  9. WebRTC源码研究(4)web服务器工作原理和常用协议基础

    文章目录 WebRTC源码研究(4)web服务器工作原理和常用协议基础 前言 做WebRTC 开发为啥要懂服务器开发知识 1. Web 服务器简介 2. Web 服务器的类型 3. Web 服务器的工 ...

最新文章

  1. jquery入门 修改网页背景颜色
  2. Android   Fragmnet的使用新体会
  3. mysql amoeba 配置_Amoeba新版本MYSQL读写分离配置
  4. 计算机是怎么知道两张图片相似的呢?
  5. 【杭电多校2020】Go Running【几何】【最小点覆盖】
  6. Win32ASm学习[1]:RadASm下测试Debug
  7. 2009微软精英挑战赛决赛
  8. Git、Github、Gitlab、Gitee、Git-ce的区别
  9. 公安部:正研究出台***犯罪司法解释
  10. 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
  11. Java 对字符以及字符数组的操作
  12. Android签名文件转化为pk8和pem
  13. STM32F103学习笔记(9)——NB-IoT模块BC26使用
  14. SAP 上线 新旧科目映射 辅助核算
  15. 简单介绍公有云、私有云、混合云
  16. 关于对接海康威视综合安防平台并使用SDK下载过车视频
  17. 多个微信服务号对接一个微信商户号流程
  18. 「数据库」Linux服务安装部署SQL Server -外网安全远程连接【端口映射】
  19. 更改office 365所有用户登录密码
  20. 基于HTML电商项目的设计与实现——html静态网站基于数码类电商购物网站网页设计与实现共计30个页面...

热门文章

  1. 记笔记非常好用的一款工具 eDiary
  2. delphi使用Foxit Quick PDF Library读写pdf文本和图片
  3. 从 Illustrator 怎么导入 Glyphs?
  4. OpenGL ES之GLSL实现多种“马赛克滤镜”效果
  5. 数论读书笔记——线性丢番图方程——解不定方程
  6. python与cad交互_python 使用pyautocad操作AutoCAD
  7. 敏捷实施步骤与价值观
  8. UICC 之 USIM 详解全系列——USIM鉴权函数说明以及鉴权向量结构
  9. swing(二十一)
  10. OpenGL初探:二维卡通人物交互设计