使用C#的Socket从头实现的带有文件上传和下载功能的HTTP服务器

使用C#和Socket从头实现的带有文件上传和下载功能的HTTP服务器。它支持GET、POST请求方法,并能处理URL参数、请求体以及文件上传和下载。

cs 复制代码
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

class HttpServer
{
    public static void Main(string[] args)
    {
        const int port = 8080;
        TcpListener listener = new TcpListener(IPAddress.Any, port);
        listener.Start();
        Console.WriteLine("HTTP server is running on port {0}...", port);

        while (true)
        {
            TcpClient client = listener.AcceptTcpClient();
            ProcessClientRequest(client);
        }
    }

    public static void ProcessClientRequest(TcpClient client)
    {
        using (NetworkStream stream = client.GetStream())
        {
            // 读取请求数据
            byte[] buffer = new byte[4096];
            int bytesRead = stream.Read(buffer, 0, buffer.Length);
            string requestString = Encoding.UTF8.GetString(buffer, 0, bytesRead);

            // 解析请求
            HttpRequest request = ParseRequest(requestString);

            // 构造响应数据
            HttpResponse response = BuildResponse(request);

            // 发送响应头
            byte[] responseHeaderBytes = Encoding.UTF8.GetBytes(response.GetHeaderString());
            stream.Write(responseHeaderBytes, 0, responseHeaderBytes.Length);

            // 发送响应体(如果有)
            if (response.ContentStream != null)
            {
                byte[] bufferBytes = new byte[4096];
                int bytesToRead;
                while ((bytesToRead = response.ContentStream.Read(bufferBytes, 0, bufferBytes.Length)) > 0)
                {
                    stream.Write(bufferBytes, 0, bytesToRead);
                }
                response.ContentStream.Close();
            }
        }

        client.Close();
    }

    public static HttpRequest ParseRequest(string requestString)
    {
        var request = new HttpRequest();

        string[] lines = requestString.Split(new[] { "\r\n" }, StringSplitOptions.None);

        // 解析请求行
        string[] requestLineParts = lines[0].Split(' ');
        request.Method = requestLineParts[0].ToUpper();
        request.Path = requestLineParts[1];

        // 解析请求头
        for (int i = 1; i < lines.Length; i++)
        {
            string[] headerParts = lines[i].Split(':');
            if (headerParts.Length == 2)
            {
                string key = headerParts[0].Trim();
                string value = headerParts[1].Trim();
                request.Headers[key] = value;
            }
        }

        // 解析请求体(仅对POST请求处理)
        if (request.Method == "POST")
        {
            int bodyIndex = Array.IndexOf(lines, "");
            if (bodyIndex != -1 && bodyIndex < lines.Length - 1)
            {
                request.Body = lines[bodyIndex + 1];
            }
        }

        return request;
    }

    public static HttpResponse BuildResponse(HttpRequest request)
    {
        var response = new HttpResponse();

        // 设置响应头信息
        response.StatusCode = 200;
        response.StatusDescription = "OK";
        response.Headers["Content-Type"] = "text/plain; charset=utf-8";

        // 处理文件上传
        if (request.Method == "POST" && request.Headers.ContainsKey("Content-Disposition"))
        {
            string filename = GetFilenameFromContentDisposition(request.Headers["Content-Disposition"]);
            using (FileStream fileStream = File.Create(filename))
            {
                using (StreamWriter writer = new StreamWriter(fileStream))
                {
                    writer.Write(request.Body);
                }
            }

            response.SetContent("File uploaded successfully.");
        }
        // 处理文件下载
        else if (request.Method == "GET" && request.Path.StartsWith("/download/"))
        {
            string filepath = request.Path.Substring("/download/".Length);
            if (File.Exists(filepath))
            {
                response.StatusCode = 200;
                response.StatusDescription = "OK";
                response.Headers["Content-Type"] = "application/octet-stream";
                response.Headers["Content-Disposition"] = $"attachment; filename=\"{Path.GetFileName(filepath)}\"";
                response.ContentStream = File.OpenRead(filepath);
            }
            else
            {
                response.StatusCode = 404;
                response.StatusDescription = "Not Found";
                response.SetContent("File not found.");
            }
        }
        // 默认返回文本内容
        else
        {
            string content = "Welcome to the HTTP server.";
            response.SetContent(content);
        }

        return response;
    }

    public static string GetFilenameFromContentDisposition(string contentDisposition)
    {
        const string keyword = "filename=\"";
        int startIndex = contentDisposition.IndexOf(keyword) + keyword.Length;
        int endIndex = contentDisposition.IndexOf("\"", startIndex);
        return contentDisposition.Substring(startIndex, endIndex - startIndex);
    }
}

class HttpRequest
{
    public string Method { get; set; }
    public string Path { get; set; }
    public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
    public string Body { get; set; }
}

class HttpResponse
{
    public int StatusCode { get; set; }
    public string StatusDescription { get; set; }
    public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
    public Stream ContentStream { get; set; }

    public void SetContent(string content)
    {
        byte[] contentBytes = Encoding.UTF8.GetBytes(content);
        ContentStream = new MemoryStream(contentBytes);
        Headers["Content-Length"] = contentBytes.Length.ToString();
    }

    public string GetHeaderString()
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendFormat("HTTP/1.1 {0} {1}\r\n", StatusCode, StatusDescription);
        foreach (var header in Headers)
        {
            builder.AppendFormat("{0}: {1}\r\n", header.Key, header.Value);
        }
        builder.Append("\r\n");
        return builder.ToString();
    }
}
相关推荐
李趣趣1 天前
C#中关于ContextMenuStrip批量添加Item的问题
开发语言·c#
数据的世界011 天前
C#权威指南第9课:方法
microsoft·c#·.net
张人玉1 天前
C# 串口通讯中 SerialPort 类的关键参数和使用方法
开发语言·c#·串口通讯
时光追逐者1 天前
一款基于 .NET WinForm 开源、轻量且功能强大的节点编辑器,采用纯 GDI+ 绘制无任何依赖库仅仅100+Kb
c#·.net·winform
sali-tec1 天前
C# 基于halcon的视觉工作流-章58-输出点云图
开发语言·人工智能·算法·计算机视觉·c#
白雪公主的后妈1 天前
Auto CAD二次开发——文字样式
c#·cad二次开发·文字样式
智者知已应修善业1 天前
【c# 想一句话把 List<List<string>>的元素合并成List<string>】2023-2-9
经验分享·笔记·算法·c#·list
FuckPatience1 天前
C# 接口隔离的一个案例
c#
津津有味道1 天前
Ntag 424 DNA写入URI网址配置开启动态UID计数器镜像C#源码
c#·uri·ndef·424dna·动态uid·计数器镜像
万19991 天前
asp.net core webapi------3.AutoMapper的使用
c#·.netcore