C#开发自己的Web服务器
下载源代码
介绍
我们将学习如何写一个简单的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服务器相关推荐
- 自主开发的小型Web服务器
自主开发的小型Web服务器 1. 技术特点 2. 具体步骤 3. CGI技术 4. Mysql连接 5. Gitee原码链接 6. 参考Blog 1. 技术特点 网络编程(http协议,TCP/IP协 ...
- 迅为IMX6ULL开发板搭建Web服务器(二)
boa 拷贝到开发板的 bin 目录下 接下来在开发板根目录下建立 www 文件夹,如图 80.15. 然后在开发板上面建立的 www 目录下面建立文件夹 cgi-bin 目录,如图 80.16: 然 ...
- arduino 网页服务器,如何将Arduino开发板用作Web服务器
通过使用以太网扩展板(Ethernet shield),您可以将Arduino开发板用作一个Web服务器. 通过向Arduino开发板配备一个以太网扩展板,您可以将其变成简易的Web服务器,并通过在与 ...
- [golang Web开发] 1.golang web开发:简介以及web服务器的搭建以及http协议简介
一.web开发简介 1.简介 Web应用在我们的生活中无处不在,看看我们日常使用的各个应用程序,它们要么是 Web应用,要么是移动 App 这类 web应用的变种,无论哪一种编程语言,只要它能够开发出 ...
- Java Web基础入门第八讲 Java Web开发入门——初始WEB服务器
WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源.Internet上供外界访问的Web资源分为: 静态web资源(如html页面):指we ...
- android与mysql数据库同步_android开发 如何通过web服务器访问MYSQL数据库并且使其数据同步到android SQLite数据库?...
通过web服务器访问MYSQL数据库有以下几个过程: 1.在MySql下创建自己的数据库和自己的表单 2.连接数据库. 3.访问数据库 1.创建web工程 (服务器端) 在Myeclipse下新建一个 ...
- go语言服务器代码,Go语言开发简单web服务器
欢迎,来自IP地址为:182.103.254.107 的朋友 Go语言由于其方便的并发通信机制以及强大的网络支持,常常被用于服务器软件的开发.本文将示例使用Go语言来开发简单的Web服务器. HTTP ...
- IIS Web 服务器/ASP.NET 运行原理基本知识概念整理
前言: 记录 IIS 相关的笔记还是从公司笔试考核题开始的,问 Application Pool 与 AppDomain 的区别? 促使我对进程池进了知识的学习,所以记录一下学习的笔记. 我们知道现在 ...
- WebRTC源码研究(4)web服务器工作原理和常用协议基础
文章目录 WebRTC源码研究(4)web服务器工作原理和常用协议基础 前言 做WebRTC 开发为啥要懂服务器开发知识 1. Web 服务器简介 2. Web 服务器的类型 3. Web 服务器的工 ...
最新文章
- jquery入门 修改网页背景颜色
- Android Fragmnet的使用新体会
- mysql amoeba 配置_Amoeba新版本MYSQL读写分离配置
- 计算机是怎么知道两张图片相似的呢?
- 【杭电多校2020】Go Running【几何】【最小点覆盖】
- Win32ASm学习[1]:RadASm下测试Debug
- 2009微软精英挑战赛决赛
- Git、Github、Gitlab、Gitee、Git-ce的区别
- 公安部:正研究出台***犯罪司法解释
- 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
- Java 对字符以及字符数组的操作
- Android签名文件转化为pk8和pem
- STM32F103学习笔记(9)——NB-IoT模块BC26使用
- SAP 上线 新旧科目映射 辅助核算
- 简单介绍公有云、私有云、混合云
- 关于对接海康威视综合安防平台并使用SDK下载过车视频
- 多个微信服务号对接一个微信商户号流程
- 「数据库」Linux服务安装部署SQL Server -外网安全远程连接【端口映射】
- 更改office 365所有用户登录密码
- 基于HTML电商项目的设计与实现——html静态网站基于数码类电商购物网站网页设计与实现共计30个页面...