C#开发自己的Web服务器

article/2025/10/22 10:28:22

 

下载源代码

 

介绍

        我们将学习如何写一个简单的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

 


http://chatgpt.dhexx.cn/article/0c37jtjN.shtml

相关文章

Web开发介绍

Web开发介绍 1 什么是web开发 Web&#xff1a;全球广域网&#xff0c;也称为万维网(www World Wide Web)&#xff0c;能够通过浏览器访问的网站。 所以Web开发说白了&#xff0c;就是开发网站的&#xff0c;例如下图所示的网站&#xff1a;淘宝&#xff0c;京东等等 那么我们…

搭建web服务器

1.要求搭建web服务器&#xff0c;能够访问到网页内容为“小胖&#xff0c;你咋这么胖呢&#xff01;” 2.要求搭建web服务器&#xff0c;创建基于域名的虚拟主机&#xff0c;能够使用www.xiaopang.com和www.dapang.com访问各自的网站网站存放路径分别为/xiaopang和/dapang,内容…

Web开发及服务器

转载自https://www.cnblogs.com/xdp-gacl/p/3729033.html。 一、基本概念 1.1、WEB开发的相关知识 WEB&#xff0c;在英语中web即表示网页的意思&#xff0c;它用于表示Internet主机上供外界访问的资源。 Internet上供外界访问的Web资源分为&#xff1a; 静态web资源&#x…

EI数据库免费检索入口

转载自&#xff1a;http://www.ei-istp.com/New_691.html 具体查询方式&#xff0c;详看链接。

数据库搜索与索引

索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她&#xff0c;则与在表中搜索所有的行相比&#xff0c;索引有助于更快地获取信息。 索引的一个主要目的就是加快检索表中数据&#x…

数据库 索引

多数数据库&#xff0c;使用 B 树&#xff08;Balance Tree&#xff09;的结构来保存索引。 B 树&#xff0c; 最上层节点&#xff1a;根节点 最下层节点&#xff1a;叶子节点 两者之间的节点&#xff1a;中间节点 B 树&#xff0c;显著特征&#xff1a;从根节点&#xff0c;到…

mysql全库搜索关键字_数据库 全文检索

一、概述 MySQL全文检索是利用查询关键字和查询列内容之间的相关度进行检索,可以利用全文索引来提高匹配的速度。 二、语法 MATCH (col1,col2,...) AGAINST (expr [search_modifier]) search_modifier: { IN BOOLEAN MODE | WITH QUERY EXPANSION } 例如:SELECT * FROM tab_n…

人文社科类文献去哪些数据库检索下载

查找下载人文社科类文献的数据库大盘点&#xff1a; 1、文献党下载器&#xff08;wxdown.org&#xff09; 大型文献馆&#xff0c;几乎整合汇集了所有中外文献数据库资源&#xff0c;可附带权限进入文献数据库查找下载文献&#xff0c;覆盖全科包括查找下载人文社科类文献的众…

数据库索引的实现原理

强烈建议参阅链接:http://www.linezing.com/blog/?p=798#nav-1 说白了,索引问题就是一个查找问题。。。 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。 在数据之外,数据库系统还维护着满足…

WoS数据库使用及检索示例

目录 快速了解一个领域的情况 1. 核心合集检索和所有数据库检索的区别 2. 检索结果分析 2.1 排序方式&#xff08;日期&#xff0c;被引频次&#xff09; 2.2 分析检索结果 2.3 精炼检索结果&#xff08;二次检索&#xff09; 2.4 创建引文报告 2.5 具体某一篇文献 快…

【MySQL】检索数据

每日鸡汤 &#xff1a; —— 若你困于无风之地&#xff0c;我将奏响高空之歌 要和我一起花 10 min 学一会 SQL 嘛&#xff1f; - 当然愿意&#xff0c;我美丽的小姐 &#xff08;封寝期间练就的自言自语能力越来越炉火纯青了~~~&#xff09; 前言&#xff1a; 本实验中所用数据…

数据库的检索(select)

今天我们学习一下数据库检索语句&#xff0c;由于经常用到&#xff0c;有需求的小伙伴欢迎来查看哦&#xff01; 一、简单的查询 --获取所以列 select * from T_table --获取部分列 select id, title from T_table 效果展示&#xff1a; 在...之间&#xff1a;Between.. a…

《MySQL必知必会》学习笔记之“数据库的检索”

文章目录 第一章 SQL与MySQL1 数据库基础2 什么是SQL3 客户机—服务器软件4 MySQL工具mysql命令行实用程序&#xff08;使用最多的实用程序之一&#xff09;MySQL AdministratorMySQL Query Browser 第二章 使用MySQL1 连接2选择数据库3了解数据库和表4 注释 第三章 检索数据1 …

最全最易理解的数据库查询教程

数据库查询 检索数据表中一个字段的内容检索数据表中多个字段的内容检索数据表中所有字段的内容带限制条件的查询表达式查询使用 WHERE 语句和逻辑表达式使用WHERE语句检索单范围数据使用WHERE语句检索双范围数据使用WHERE语句检索不匹配某条件的语句使用通配符[]模糊匹配数据内…

数据库基础知识——SELECT 语句(检索数据)

SQL使用 SQL&#xff08;发音为字母S-Q-L或sequel&#xff09;是结构化查询语言&#xff08;Structured Query Language&#xff09;的缩写。SQL是一种专门用来与数据库通信的语言。 SQL 语言特点&#xff1a; SQL 语言不区分大小写&#xff1b;在命令行窗口的 SQL 语句要以…

Python爬虫获取数据保存到数据库中(超级详细,保证一看就会)

1.简介介绍 -网络爬虫&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追逐者&#xff09;&#xff0c;是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动…

python数据爬取---简单页面的爬取

1、准备Requests和User Agent python中能实现爬虫功能的库有若干个&#xff0c;而最简单最容易上手的&#xff0c;要数Requests库&#xff0c;它是一个常用的http请求库&#xff0c;首先用pip install requests 进行安装才能使用。 User Agent一般翻译为用户代理&#xff0c;…

Python小姿势 - # 如何使用Python爬取网页数据

如何使用Python爬取网页数据 今天我们来学习一下如何使用Python来爬取网页数据。 首先&#xff0c;我们需要准备一个空白的文件&#xff0c;在文件中输入以下代码&#xff1a; import requests url http://www.baidu.com r requests.get(url) print(r.text) 上面的代码中&…

Python爬取数据

爬虫基本思路<通用> Response 服务器返回响应数据 Preview 预览 Payload 请求参数 Headers 头部信息&#xff1a;请求url地址、请求方式、响应头、请求头 一、数据来源分析 -抓包分析我们想要的数据内容&#xff0c;请求的那个网页 url地址得到。 -选中xhr 找到想要的内容…

手把手教会你用Python爬虫爬取网页数据!!

其实在当今社会&#xff0c;网络上充斥着大量有用的数据&#xff0c;我们只需要耐心的观察&#xff0c;再加上一些技术手段&#xff0c;就可以获取到大量的有价值数据。这里的“技术手段”就是网络爬虫。今天就给大家分享一篇爬虫基础知识和入门教程&#xff1a; 什么是爬虫&a…