前言
嗨,各位程序员小伙伴们,大家早上好啊!️
在现代软件开发中,我们习惯了使用 ASP.NET Core、Spring Boot 这样强大的框架。
只要敲下几行代码,配置好路由,一个功能完备的 Web 服务器就搭好了。
这种“开箱即用”的体验固然爽,但久而久之,Web 开发在我们眼里就成了一个“黑盒”。
你有没有想过:
当你在浏览器地址栏输入网址并按下回车的那一瞬间,到底发生了什么?
服务器又是如何精准地知道你想要哪个页面的?
其实,剥开那些复杂的框架外衣,Web 的本质无非就是 TCP 连接 + 文本协议。
今天,老杨就带大家跳出舒适区,用最底层的 C# Socket,从 0 手写一个简易的 HTTP 服务器。
相信我,当你亲自构造出 HTTP 响应报文的那一刻,你对网络编程的理解绝对会上升到一个新的高度!
废话不多说,让我们打开 Visual Studio,开始这场“造轮子”之旅!
Step by Step 步骤
1. 创建控制台项目,命名为 SimpleHttpServer
2. 引入 Socket 相关的命名空间
打开 Program.cs 文件,在顶部添加:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
3. 编写主逻辑
实现一个循环,不断接受客户端连接,并为每个连接启动一个新线程处理,留意代码中的注释:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SimpleHttpServer
{
class Program
{
static void Main(string[] args)
{
// 1. 定义监听的 IP 地址和端口号
// 这里监听本机所有 IP 地址(IPv4),端口 8080
IPAddress ip = IPAddress.Any;
int port = 8080;
// 2. 创建 TcpListener 对象,开始监听
TcpListener listener = new TcpListener(ip, port);
listener.Start();
Console.WriteLine($"HTTP 服务器已启动,监听 {ip}:{port}");
Console.WriteLine("请在浏览器中访问 http://localhost:8080/");
// 3. 无限循环,等待客户端连接
while (true)
{
// 4. 接受一个客户端连接(阻塞直到有连接)
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine($"客户端已连接:{client.Client.RemoteEndPoint}");
// 5. 为每个客户端创建一个新线程处理,避免阻塞后续连接
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
}
/// <summary>
/// 处理单个客户端请求的方法(由线程池调用)
/// </summary>
/// <param name="state">传入的 TcpClient 对象</param>
static void HandleClient(object state)
{
TcpClient client = (TcpClient)state;
// 确保使用完释放资源
using (client)
{
try
{
// 获取网络流,用于读写数据
NetworkStream stream = client.GetStream();
// 1. 读取客户端发来的 HTTP 请求
// 先分配一个缓冲区,读取前 1024 字节,通常足够包含请求行和头部
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
// 将收到的字节转换为字符串(只取实际读取的部分)
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine("收到请求:");
Console.WriteLine(request.Substring(0, Math.Min(request.Length, 200))); // 只打印前200字符
// 2. 解析请求行(第一行)
// 请求行格式:METHOD /path HTTP/1.1
string firstLine = request.Substring(0, request.IndexOf("\r\n"));
string[] parts = firstLine.Split(' ');
string method = parts[0]; // GET / POST 等
string path = parts[1]; // 请求的资源路径,如 "/" 或 "/index.html"
Console.WriteLine($"请求方法:{method},路径:{path}");
// 3. 根据路径构造不同的响应内容
string responseBody;
if (path == "/")
{
responseBody = "<html><body><h1>欢迎来到简易 HTTP 服务器</h1><p>这是根路径</p></body></html>";
}
else if (path == "/about")
{
responseBody = "<html><body><h1>关于页面</h1><p>这是一个用 .NET Framework 4.8 Socket 实现的服务器</p></body></html>";
}
else
{
// 404 Not Found
responseBody = "<html><body><h1>404 - 页面未找到</h1></body></html>";
}
// 4. 构造 HTTP 响应报文
// 状态行:HTTP/1.1 200 OK(或 404 Not Found)
// 响应头:Content-Type, Content-Length, Connection 等
// 空行分隔头和体
string statusLine;
if (path == "/" || path == "/about")
statusLine = "HTTP/1.1 200 OK\r\n";
else
statusLine = "HTTP/1.1 404 Not Found\r\n";
string contentType = "Content-Type: text/html; charset=utf-8\r\n";
string contentLength = $"Content-Length: {Encoding.UTF8.GetByteCount(responseBody)}\r\n";
string connection = "Connection: close\r\n"; // 短连接,处理完即关闭
string blankLine = "\r\n";
// 拼接完整的响应字符串
// 注意:响应头之后必须有一个空行
string responseString = statusLine + contentType + contentLength + connection + blankLine + responseBody;
// 5. 将响应字符串转为字节数组并发送给客户端
byte[] responseBytes = Encoding.UTF8.GetBytes(responseString);
stream.Write(responseBytes, 0, responseBytes.Length);
stream.Flush(); // 确保数据立即发送
Console.WriteLine("响应已发送,关闭连接。");
}
catch (Exception ex)
{
Console.WriteLine("处理客户端时出错:" + ex.Message);
}
}
}
}
}
4. 运行与测试
按 F5 启动程序,控制台会显示:
HTTP 服务器已启动,监听 0.0.0.0:8080
请在浏览器中访问 http://localhost:8080/打开浏览器,访问
http://localhost:8080/,应该看到:欢迎来到简易 HTTP 服务器
这是根路径访问
http://localhost:8080/about,看到:关于页面
这是一个用 .NET Framework 4.8 Socket 实现的服务器访问其他任意路径(如
http://localhost:8080/test),看到 404 页面。观察控制台输出,可以看到每次请求的信息。
总结
好了,今天的分享就到这里啦!
通过这几十行代码,我们其实已经抓住了 Web 开发的“灵魂”。
你会发现,所谓的 HTTP 协议,本质上就是客户端和服务器之间按照特定格式(请求行、请求头、空行、正文)进行的文本对话。
当然,我们今天写的只是一个“玩具级”的服务器。它没有处理并发异常、没有支持长连接(Keep-Alive)、也没有处理复杂的 POST 请求体。在真实的生产环境中,我们依然需要依赖 ASP.NET Core 或 Nginx 这样成熟的工业级组件。
但是,“知其然,更要知其所以然”。
当你以后在排查复杂的网络问题,或者需要优化服务器性能时,今天这段底层 Socket 编程的经验,绝对会成为你最强有力的武器!
如果觉得这篇文章让你对网络编程有了新的认识,别忘了点个【赞与在看】哦,你的支持是我最大的动力!
我们下期技术分享再见!
如果你有更好的想法或建议,欢迎留言讨论!
关键词
C#、Socket、网络编程、Web底层