使用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();
    }
}
相关推荐
游乐码8 小时前
c#变长关键字和参数默认值
学习·c#
全栈小59 小时前
【C#】合理使用DeepSeek相关AI应用为我们提供强有力的开发工具,在.net core 6.0框架下使用JsonNode动态解析json字符串,如何正确使用单问号和双问号做好空值处理
人工智能·c#·json·.netcore·deepseek
wearegogog1239 小时前
基于C#的TCP/IP通信客户端与服务器
服务器·tcp/ip·c#
A_nanda15 小时前
C#调用Quartz.NET的完整实现。
c#·.net·quartz
2501_9307077815 小时前
使用C#代码在 PowerPoint 演示文稿中插入表格
开发语言·c#·powerpoint
少控科技16 小时前
C#基础训练营 - 01 - 数据类型
开发语言·c#
1314lay_100716 小时前
Vue3 + Element Plus项目和C# .Net 7.0 Core后端API项目发布部署到服务器
服务器·前端·javascript·vue.js·elementui·c#·.net
郝亚军16 小时前
c#如何编译、通过icd文件生成static_model.c和static_model.h
开发语言·c#
Traced back17 小时前
保姆级C#进阶教程:从入门到企业级开发,小白也能秒懂!
开发语言·c#
柒儿吖17 小时前
CharLS 无损 JPEG-LS 库在 OpenHarmony 的 lycium 适配与 ctest 验证
c++·华为·c#·harmonyos