使用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 小时前
20.抽卡只有金,带保底(WPF) C#
ui·c#·wpf
向宇it11 小时前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
向宇it13 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
坐井观老天17 小时前
在C#中使用资源保存图像和文本和其他数据并在运行时加载
开发语言·c#
pchmi20 小时前
C# OpenCV机器视觉:模板匹配
opencv·c#·机器视觉
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭21 小时前
C#都可以找哪些工作?
开发语言·c#
boligongzhu1 天前
Dalsa线阵CCD相机使用开发手册
c#
向宇it1 天前
【从零开始入门unity游戏开发之——C#篇23】C#面向对象继承——`as`类型转化和`is`类型检查、向上转型和向下转型、里氏替换原则(LSP)
java·开发语言·unity·c#·游戏引擎·里氏替换原则
sukalot1 天前
windows C#-命名实参和可选实参(下)
windows·c#