C# / C++ / Java 可用的局域网 / 公网文件服务器!(绝对可用)

目录

1. 前言

2. C#部分

说明

用法

代码

3. C++部分

说明 (如果运行不成功看这里)

用法

代码

Main.cpp

Logger.h

ColorUtil.h

4. Java部分

说明

代码

5. 结语


1. 前言

好久没发博客了,正好我看到以前写了一个文件服务器,最近又用Java和C++改写了一下,就发出来了 : )

只需要打开程序,在指定目录放些文件,就可实现在局域网中文件互传的功能。

如果想在公网使用,那你必须要有公网IP或者一个FRP,或者直接丢服务器上 (有服务器的基本上不会用我这么垃的文件服务器)

2. C#部分

说明

我分了几个文件,还使用了资源文件和自己写的Logger,所以没办法将代码一次性贴出来,我这里修改一下,整合到一段代码中,使它直接复制粘贴就可用。我这里直接用英文了,应该都看得懂。

代码很长,请谅解。

用法

在同一局域网下的设备直接打开浏览器,输入控制台输出的IP和端口号,即可访问。(127.0.0.1和公网IP都可以访问) 然后点击要下载的文件,即可下载。

格式:IP:端口 (英文冒号)

注意,在有多个网络适配器的情况下IP可能输出不正确,这时候查看ipconfig(cmd输入ipconfig),想在哪个局域网内共享就用哪个IP。

文件名不能有空格!!!

文件名不能有空格!!!

文件名不能有空格!!!

代码

using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Resources;

namespace FileServer
{
	class Program
	{
		private const string ResourcesPath = "D:\\Resources\\";
		private const int MaxConcurrentRequests = 100;
		private static ushort Port = 1012;
		private static bool IsRootPath = false;
		private static readonly ushort[] UnSafePorts = { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669 };

		private static readonly ConcurrentDictionary<string, byte[]> ResourceCache = new();
		//private static readonly ResourceManager Language_Resources = new ("FileServer.Resources", typeof(Program).Assembly);

		static void Main(string[] args)
		{
			if (args.Length > 0) _ = ushort.TryParse(args[0], out Port);
			if (Port == 0) Port++;
			if (Port == 65535) Port = 1;
			foreach (var UnSafePort in UnSafePorts)
			{
				if (Port == 0) Port++;
				if (Port == 65535) Port = 1;
				if (Port == UnSafePort) Port++;
			}
			while (true)
			{
				Logger.Info("Server Starting!");
				var LocalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), Port);
				var Listener = new TcpListener(LocalEndPoint);
				try
				{
					Listener.Start();
					IPAddress? LocalIPAddress = null;
					string HostName = Dns.GetHostName();
					IPHostEntry IPHostInfo = Dns.GetHostEntry(HostName);
					foreach (IPAddress Address in IPHostInfo.AddressList)
					{
						if (Address.AddressFamily == AddressFamily.InterNetwork)
						{
							LocalIPAddress = Address;
							break;
						}
					}
					string LocalIp = LocalIPAddress.ToString();
					Logger.Info("IP: " + LocalIp);
					Logger.Info("Server Started On Port " + Port + ".");


					var semaphore = new Semaphore(MaxConcurrentRequests, MaxConcurrentRequests);

					while (true)
					{
						Logger.Info("Waiting Clients...");

						semaphore.WaitOne();

						var client = Listener.AcceptTcpClient();

						Logger.Info("Client Connected. IP: " + client.Client.RemoteEndPoint);

						ThreadPool.QueueUserWorkItem(HandleRequest, new RequestState(client, semaphore));
					}
				}
				catch (SocketException)
				{
					Port++;
				}
				catch (Exception e)
				{
					Logger.Err("Error Occurred: " + e);
				}
				finally
				{
					Listener.Stop();
				}
			}
		}

		private static void HandleRequest(object State)
		{
			var Request = (RequestState)State;
			var Client = Request.Client;
			var Sign = Request.Semaphore;

			try
			{
				var RequestStream = Client.GetStream();
				var Reader = new StreamReader(RequestStream);
				var RequestLine = Reader.ReadLine();

				if (RequestLine != null)
				{
					var Tokens = RequestLine.Split(' ');
					var Method = Tokens[0];
					var Path = Tokens[1];

					if (Method == "GET")
					{
						if (Path == "/")
						{
							IsRootPath = true;
							Logger.Info("Client Requested Root Path.");
							var FilesHtml = GetFilesHtml();
							var ContentBytes = Encoding.UTF8.GetBytes(FilesHtml);
							SendResponse(Client, ContentBytes);
						}
						if (ResourceCache.TryGetValue(Path, out var ResourceBytes))
						{
							SendResponse(Client, ResourceBytes);
						}
						else
						{
							ResourceBytes = LoadResource(Path);
							ResourceCache.TryAdd(Path, ResourceBytes);
							SendResponse(Client, ResourceBytes);
						}

					}
				}

				Client.Close();
			}
			catch (Exception e)
			{
				Logger.Err("Error Occurred: " + e.Message);
			}
			finally
			{
				Sign.Release();
			}
		}
		private static string GetFilesHtml()
		{
			var Website = new StringBuilder();
			Website.Append("<html>");
			Website.Append("<meta charset=\"UTF-8\">");
			Website.Append("<head><title>HXLyxx - Super File Server</title></head>");
			Website.Append("<body>");
			Website.Append("<h2>Server Root Directory</h2>");
			Website.Append("<ul>");

			var RootPath = Path.Combine(ResourcesPath);
			var Files = Directory.GetFiles(RootPath);
			foreach (var File in Files)
			{
				var FileName = Path.GetFileName(File);
				Website.AppendFormat("<li><a href=\"/{0}\">{1}</a></li>", FileName, FileName);
			}

			Website.Append("</ul>");
			Website.Append("</body>");
			Website.Append("</html>");

			return Website.ToString();
		}

		private static byte[] LoadResource(string RelativePath)
		{
			string NotFoundMessage;
			var FullPath = Path.Combine(ResourcesPath, RelativePath.TrimStart('/'));

			Logger.Info("Loading Resource..." + FullPath);

			byte[] Content;
			if (File.Exists(FullPath))
			{
				Logger.Info($"{FullPath} Ready To Transfer!");
				using FileStream Transfer = new(FullPath, FileMode.Open, FileAccess.Read);
				using BinaryReader Reader = new(Transfer);
				Logger.Info($"{FullPath} Is Transferring!");
				Content = Reader.ReadBytes((int)Transfer.Length);
				Logger.Info($"{FullPath} Transferred!");
				IsRootPath = false;
			}
			else if (IsRootPath)
			{
				Content = Encoding.UTF8.GetBytes(string.Empty);
				IsRootPath = false;
			}
			else
			{
				Logger.Warn($"{FullPath} Does Not Exist!");
				NotFoundMessage = $"<html><meta charset=\"UTF-8\"><head><title>404</title></head><body><h1>404 Not Found</h1><br><h2>{RelativePath}</h2></body></html>";
				Content = Encoding.UTF8.GetBytes(NotFoundMessage);
				IsRootPath = false;
			}
			return Content;


		}

		private static void SendResponse(TcpClient Client, byte[] ResourceBytes)
		{
			var ETag = CalculateEtag(ResourceBytes);

			Logger.Info($"Sending Response...");
			Logger.Info("ETag", ETag);

			var ResponseStream = Client.GetStream();
			var Writer = new StreamWriter(ResponseStream);

			Writer.WriteLine("HTTP/1.1 200 OK");
			Writer.WriteLine();
			Writer.Flush();

			ResponseStream.Write(ResourceBytes, 0, ResourceBytes.Length);
		}

		private static string CalculateEtag(byte[] Data)
		{
			using var Sha256 = SHA256.Create();
			var Hash = Sha256.ComputeHash(Data);
			return Convert.ToBase64String(Hash);
		}

		private class RequestState
		{
			public TcpClient Client { get; }
			public Semaphore Semaphore { get; }

			public RequestState(TcpClient client, Semaphore semaphore)
			{
				Client = client;
				Semaphore = semaphore;
			}
		}
	}

	public class Logger
	{
		private static string GetTime()
		{
			return DateTime.Now.ToString("[HH:mm:ss.ffff] ");
		}
		private static string GetType(string Type)
		{
			return Type switch
			{
				"I" => "[Info] ",
				"W" => "[Warn] ",
				"E" => "[Err] ",
				_ => string.Empty,
			};
		}
		public static void UpdateScreen()
		{
			Console.SetCursorPosition(0, Console.CursorTop);
			SetColor(ColorType.Aqua);
			Console.Write(">>> ");
		}
		public static void Info<Template>(Template message)
		{
			Log("I", message);
		}
		public static void Info<Template>(Template prefix, Template message)
		{
			Log("I", prefix, message);
		}
		public static void Info<Template>(ColorType color, Template message)
		{
			Log("I", color, message);
		}
		public static void Info<Template>(ColorType color, Template prefix, Template message)
		{
			Log("I", color, prefix, message);
		}
		public static void Warn<Template>(Template message)
		{
			Log("W", message);
		}
		public static void Warn<Template>(Template prefix, Template message)
		{
			Log("W", prefix, message);
		}
		public static void Warn<Template>(ColorType color, Template message)
		{
			Log("W", color, message);
		}
		public static void Warn<Template>(ColorType color, Template prefix, Template message)
		{
			Log("W", color, prefix, message);
		}
		public static void Err<Template>(Template message)
		{
			Log("E", message);
		}
		public static void Err<Template>(Template prefix, Template message)
		{
			Log("E", prefix, message);
		}
		public static void Err<Template>(ColorType color, Template message)
		{
			Log("E", color, message);
		}
		public static void Err<Template>(ColorType color, Template prefix, Template message)
		{
			Log("E", color, prefix, message);
		}

		private static void Log<Template>(string type, Template message)
		{
			Console.ForegroundColor = type switch
			{
				"I" => ConsoleColor.Green,
				"W" => ConsoleColor.DarkYellow,
				"E" => ConsoleColor.Red,
				_ => Console.ForegroundColor
			};
			Console.WriteLine(GetTime() + GetType(type) + message);
		}
		private static void Log<Template>(string type, Template prefix, Template message)
		{
			Console.ForegroundColor = type switch
			{
				"I" => ConsoleColor.Green,
				"W" => ConsoleColor.DarkYellow,
				"E" => ConsoleColor.Red,
				_ => Console.ForegroundColor
			};
			Console.WriteLine(GetTime() + "[" + prefix + "] " + message);
		}
		private static void Log<Template>(string type, ColorType color, Template message)
		{
			SetColor(color);
			Console.WriteLine(GetTime() + GetType(type) + message);
		}
		private static void Log<Template>(string type, ColorType color, Template prefix, Template message)
		{
			SetColor(color);
			Console.WriteLine(GetTime() + "[" + prefix + "] " + message);
		}


		public static void SetColor(ColorType Color)
		{
			Console.ForegroundColor = Color switch
			{
				ColorType.Black => ConsoleColor.Black,
				ColorType.DarkBlue => ConsoleColor.DarkBlue,
				ColorType.DarkGreen => ConsoleColor.DarkGreen,
				ColorType.DarkAqua => ConsoleColor.DarkCyan,
				ColorType.DarkRed => ConsoleColor.DarkRed,
				ColorType.DarkMagenta => ConsoleColor.DarkMagenta,
				ColorType.Yellow => ConsoleColor.DarkYellow,
				ColorType.Gray => ConsoleColor.Gray,
				ColorType.DarkGray => ConsoleColor.DarkGray,
				ColorType.Blue => ConsoleColor.Blue,
				ColorType.Green => ConsoleColor.Green,
				ColorType.Aqua => ConsoleColor.Cyan,
				ColorType.Red => ConsoleColor.Red,
				ColorType.Magenta => ConsoleColor.Magenta,
				ColorType.LightYellow => ConsoleColor.Yellow,
				ColorType.White => ConsoleColor.White,
				_ => ConsoleColor.White,
			};
		}
	}

	public enum ColorType
	{
		Black = 0,
		DarkBlue = 1,
		DarkGreen = 2,
		DarkAqua = 3,
		DarkRed = 4,
		DarkMagenta = 5,
		Yellow = 6,
		Gray = 7,
		DarkGray = 8,
		Blue = 9,
		Green = 10,
		Aqua = 11,
		Red = 12,
		Magenta = 13,
		LightYellow = 14,
		White = 15
	}
}

3. C++部分

说明 (如果运行不成功看这里)

C++我不好,所以编的不好请见谅 =w=

注意:我是在Visual Studio 2022中编写的,使用其他编译器可能会报错。我推荐用MinGW GCC,下载新版本 (支持C++20的) ,然后获取本机IP的方法中有可能报错,那就换一个方法。 

MinGW新版本下载:
Releases · skeeto/w64devkit (github.com)https://github.com/skeeto/w64devkit/releases

我本来用的是这个方法 (在MSVC编译器下可用) :C++获取本机IP地址(Win+linux) - Truman001 - 博客园 (cnblogs.com)https://www.cnblogs.com/LuckCoder/p/14310539.html

如果不行就用这个方法 (在GCC13.2编译器下可用) :
windows使用C++获取本机IP地址 - Demon90s - 博客园 (cnblogs.com)https://www.cnblogs.com/demon90s/p/15587773.html

只需要修改获取IP的方法就可以

And, 在Visual Studio中,需要右键项项目 → 属性 → 配置属性 → 常规 → C++语言标准

将标准至少改成C++20

Enjoy : )

用法

与C#版本的一致,直接去上面看吧

代码

因为我将几个文件合并起来一堆报错,所以我就一个一个发吧(真的好长

Main.cpp

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <thread>
#include <filesystem>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iomanip>
#include <optional>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "Logger.h"

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "IPHLPAPI.lib")

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))


const std::string ResourcesPath = "D:\\Resources\\";
const int MaxConcurrentRequests = 100;
static unsigned short Port = 1012;
const bool IsRootPath = false;

const unsigned short UnSafePorts[] = { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669 };

std::map<std::string, std::vector<char>> ResourceCache;
HMODULE Language_Resources;

using namespace std;

std::string GetFilesHtml()
{
    std::stringstream website;
    website << "<html>";
    website << "<meta charset=\"UTF - 8\">";
    website << "<head><title>HXLyxx's Super File Server (C++ Version!)</title></head>";
    website << "<body>";
    website << "<h2>Server Root Path</h2>";
    website << "<h3>FileServer CPP Version</h3>";
    website << "<h4>By HXLyxx!</h4>";
    website << "<ul>";

    std::string rootPath = ResourcesPath;
    WIN32_FIND_DATAA findData;
    HANDLE hFind = FindFirstFileA((rootPath + "*").c_str(), &findData);
    if (hFind != INVALID_HANDLE_VALUE)
    {
        do
        {
            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                continue;

            website << "<li><a href=\"/" << findData.cFileName << "\">" << findData.cFileName << "</a></li>";
        } while (FindNextFileA(hFind, &findData));

        FindClose(hFind);
    }

    website << "</ul>";
    website << "</body>";
    website << "</html>";

    return website.str();
}

std::vector<char> LoadResource(const std::string& relativePath)
{
    std::string fullPath = ResourcesPath + relativePath.substr(1);

    Logger::Info("Loading Resource: " + fullPath + ".");

    std::ifstream file(fullPath, std::ios::binary);
    if (file)
    {
        std::vector<char> content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        Logger::Info(fullPath + " ready to transfer!");
        return content;
    }
    else
    {
        Logger::Warn(fullPath + " does not exist!");
        std::string notFoundMessage = "<html><meta charset=\"UTF-8\"><head><title>404</title></head><body><h1>Unable To Find The Resource.</h1><br><h2>" + relativePath + "</h2></body></html>";
        return std::vector<char>(notFoundMessage.begin(), notFoundMessage.end());
    }
}

std::string CalculateEtag(const std::vector<char>& data)
{
    HCRYPTPROV hProv;
    HCRYPTHASH hHash;
    BYTE rgbHash[32];
    DWORD cbHash = 32;

    if (CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
    {
        if (CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
        {
            if (CryptHashData(hHash, reinterpret_cast<const BYTE*>(data.data()), static_cast<DWORD>(data.size()), 0))
            {
                if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
                {
                    std::stringstream ss;
                    for (DWORD i = 0; i < cbHash; i++)
                    {
                        ss << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << static_cast<int>(rgbHash[i]);
                    }
                    CryptDestroyHash(hHash);
                    CryptReleaseContext(hProv, 0);
                    return ss.str();
                }
            }
            CryptDestroyHash(hHash);
        }
        CryptReleaseContext(hProv, 0);
    }

    return "";
}

void SendResponse(SOCKET clientSocket, const std::vector<char>& resourceBytes)
{
    std::string etag = CalculateEtag(resourceBytes);

    Logger::Info("Sending Response...");
    Logger::Info("ETag: " + etag);

    std::string httpResponse = "HTTP/1.1 200 OK\r\n";
    httpResponse += "Content-Type: text/html\r\n"; // Set the appropriate content type for the resource.
    httpResponse += "ETag: " + etag + "\r\n";
    httpResponse += "\r\n";

    send(clientSocket, httpResponse.c_str(), httpResponse.length(), 0);
    send(clientSocket, resourceBytes.data(), resourceBytes.size(), 0);
}

void HandleRequest(SOCKET clientSocket)
{
    char buffer[4096];
    memset(buffer, 0, sizeof(buffer));

    int bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
    if (bytesRead > 0)
    {
        std::string request(buffer);

        size_t pos = request.find("\r\n");
        if (pos != std::string::npos)
        {
            std::string requestLine = request.substr(0, pos);
            std::istringstream iss(requestLine);

            std::vector<std::string> tokens;
            std::string token;
            while (std::getline(iss, token, ' '))
            {
                tokens.push_back(token);
            }

            if (tokens.size() >= 2)
            {
                std::string method = tokens[0];
                std::string path = tokens[1];

                if (method == "GET")
                {
                    if (path == "/")
                    {
                        std::string filesHtml = GetFilesHtml();
                        std::vector<char> contentBytes(filesHtml.begin(), filesHtml.end());

                        std::string httpResponse = "HTTP/1.1 200 OK\r\n";
                        httpResponse += "Content-Type: text/html\r\n";
                        httpResponse += "Content-Length: " + std::to_string(contentBytes.size()) + "\r\n";
                        httpResponse += "\r\n";
                        httpResponse += filesHtml;

                        send(clientSocket, httpResponse.c_str(), httpResponse.length(), 0);
                    }
                    else
                    {
                        std::string fullPath = ResourcesPath + path.substr(1);

                        if (ResourceCache.count(fullPath) > 0)
                        {
                            std::vector<char>& resourceBytes = ResourceCache[fullPath];

                            std::string etag = CalculateEtag(resourceBytes);

                            std::filesystem::path filePath(fullPath);
                            std::string filename = filePath.filename().string();

                            std::string httpResponse = "HTTP/1.1 200 OK\r\n";
                            httpResponse += "Content-Type: application/octet-stream\r\n";
                            httpResponse += "Content-Length: " + std::to_string(resourceBytes.size()) + "\r\n";
                            httpResponse += "Content-Disposition: attachment; filename=\"" + filename + "\"\r\n";
                            httpResponse += "ETag: " + etag + "\r\n";
                            httpResponse += "\r\n";

                            send(clientSocket, httpResponse.c_str(), httpResponse.length(), 0);
                            send(clientSocket, resourceBytes.data(), resourceBytes.size(), 0);
                        }
                        else
                        {
                            std::vector<char> resourceBytes = LoadResource(path);
                            ResourceCache[fullPath] = resourceBytes;

                            std::string etag = CalculateEtag(resourceBytes);

                            std::filesystem::path filePath(fullPath);
                            std::string filename = filePath.filename().string();

                            std::string httpResponse = "HTTP/1.1 200 OK\r\n";
                            httpResponse += "Content-Type: application/octet-stream\r\n";
                            httpResponse += "Content-Length: " + std::to_string(resourceBytes.size()) + "\r\n";
                            httpResponse += "Content-Disposition: attachment; filename=\"" + filename + "\"\r\n";
                            httpResponse += "ETag: " + etag + "\r\n";
                            httpResponse += "\r\n";

                            send(clientSocket, httpResponse.c_str(), httpResponse.length(), 0);
                            send(clientSocket, resourceBytes.data(), resourceBytes.size(), 0);
                        }
                    }
                }
            }
        }
    }

    closesocket(clientSocket);
}

std::optional<std::string> GetIpAddress()
{
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = (IP_ADAPTER_INFO*)MALLOC(sizeof(IP_ADAPTER_INFO));
    if (pAdapterInfo == NULL) {
        return std::nullopt;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        FREE(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)MALLOC(ulOutBufLen);
        if (pAdapterInfo == NULL) {
            return std::nullopt;
        }
    }

    std::string ip_address;
    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        pAdapter = pAdapterInfo;
        if (pAdapter) {
            ip_address = pAdapter->IpAddressList.IpAddress.String;
        }
    }

    if (pAdapterInfo) {
        FREE(pAdapterInfo);
    }

    if (!ip_address.empty()) {
        return ip_address;
    }
    return std::nullopt;
}

int main(int argc, char* argv[])
{
    if (argc > 1) Port = std::stoi(argv[1]);
    if (Port == 0) Port++;
    if (Port == 65535) Port = 1;
    for (const auto& UnSafePort : UnSafePorts)
    {
        if (Port == 0) Port++;
        if (Port == 65535) Port = 1;
        if (Port == UnSafePort) Port++;
    }

    ServerStart:

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        Logger::Err("Failed to initialize Winsock.");
        return 1;
    }

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == INVALID_SOCKET)
    {
        Logger::Err("Failed to create server socket.");
        return 1;
    }

    sockaddr_in serverAddress{};
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(Port);

    if (bind(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) == SOCKET_ERROR)
    {
        Logger::Err("Failed to bind server socket.");
        closesocket(serverSocket);
        WSACleanup();
        Port++;
        Logger::Warn("The Default Port Already In Use. The Port Will +1.");
        goto ServerStart;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        Logger::Err("Failed to listen on server socket.");
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    Logger::Info("Welcome To Use Linx-Web.FileServer (C++ Version) !");
    Logger::Info("By HXLyxx!");

    auto res = GetIpAddress();
    string IP;
    if (res.has_value())
    {
        IP = res.value();
    }

    Logger::Info("Server Started.");
    Logger::Info("IP: " + IP + ":" + std::to_string(Port));

    while (true)
    {
        Logger::Info("Waiting for clients...");

        SOCKET clientSocket = accept(serverSocket, nullptr, nullptr);
        if (clientSocket == INVALID_SOCKET)
        {
            Logger::Err("Failed to accept client socket.");
            continue;
        }

        sockaddr_in clientAddr;
        int addrLen = sizeof(clientAddr);
        getpeername(clientSocket, (sockaddr*)&clientAddr, &addrLen);

        char clientIP[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN);
        string Temp = clientIP;
        int clientPort = ntohs(clientAddr.sin_port);

        Logger::Info("Client connected, IP: " + Temp + ":" + std::to_string(clientPort));

        std::thread requestThread(HandleRequest, clientSocket);
        requestThread.detach();
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

Logger.h

#pragma once
#include <iostream>
#include <chrono>
#include <iomanip>
#include "ColorUtil.h"

class Logger
{
private:
    static std::string GetTime()
    {
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        char buffer[100];
        tm timeInfo;
        localtime_s(&timeInfo, &time);
        std::strftime(buffer, sizeof(buffer), "[%H:%M:%S] ", &timeInfo);
        return std::string(buffer);
    }

    static std::string GetType(const std::string& type)
    {
        if (type == "I")
        {
            return "[Info] ";
        }
        else if (type == "W")
        {
            return "[Warn] ";
        }
        else if (type == "E")
        {
            return "[Err] ";
        }
        else
        {
            return "";
        }
    }

    template <typename Template>
    static void Log(const std::string& type, const Template& message)
    {
        std::cout << GetTime() << GetType(type) << message << std::endl;
    }

    template <typename Template>
    static void Log(const std::string& type, const Template& prefix, const Template& message)
    {
        std::cout << GetTime() << "[" << prefix << "] " << message << std::endl;
    }

    template <typename Template>
    static void Log(const std::string& type, ColorType color, const Template& message)
    {
        SetColor(color);
        std::cout << GetTime() << GetType(type) << message << std::endl;
    }

    template <typename Template>
    static void Log(const std::string& type, ColorType color, const Template& prefix, const Template& message)
    {
        SetColor(color);
        std::cout << GetTime() << "[" << prefix << "] " << message << std::endl;
    }

public:
    static void UpdateScreen()
    {
        std::cout << ">>> ";
    }

    template <typename Template>
    static void Info(const Template& message)
    {
        SetColor(ColorType::Green);
        Log("I", message);
    }

    template <typename Template>
    static void Info(const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Green);
        Log("I", prefix, message);
    }

    template <typename Template>
    static void Info(ColorType color, const Template& message)
    {
        SetColor(ColorType::Green);
        Log("I", color, message);
    }

    template <typename Template>
    static void Info(ColorType color, const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Green);
        Log("I", color, prefix, message);
    }

    template <typename Template>
    static void Warn(const Template& message)
    {
        SetColor(ColorType::Yellow);
        Log("W", message);
    }

    template <typename Template>
    static void Warn(const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Yellow);
        Log("W", prefix, message);
    }

    template <typename Template>
    static void Warn(ColorType color, const Template& message)
    {
        SetColor(ColorType::Yellow);
        Log("W", color, message);
    }

    template <typename Template>
    static void Warn(ColorType color, const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Yellow);
        Log("W", color, prefix, message);
    }

    template <typename Template>
    static void Err(const Template& message)
    {
        SetColor(ColorType::Red);
        Log("E", message);
    }

    template <typename Template>
    static void Err(const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Red);
        Log("E", prefix, message);
    }

    template <typename Template>
    static void Err(ColorType color, const Template& message)
    {
        SetColor(ColorType::Red);
        Log("E", color, message);
    }

    template <typename Template>
    static void Err(ColorType color, const Template& prefix, const Template& message)
    {
        SetColor(ColorType::Red);
        Log("E", color, prefix, message);
    }
};

ColorUtil.h

#pragma once

enum class ColorType
{
    Black = 0,
    DarkBlue = 1,
    DarkGreen = 2,
    DarkAqua = 3,
    DarkRed = 4,
    DarkMagenta = 5,
    Yellow = 6,
    Gray = 7,
    DarkGray = 8,
    Blue = 9,
    Green = 10,
    Aqua = 11,
    Red = 12,
    Magenta = 13,
    LightYellow = 14,
    White = 15
};



static void SetColor(ColorType color)
{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    WORD colorCode;
    switch (color)
    {
    case ColorType::Black:
        colorCode = 0;
        break;
    case ColorType::DarkBlue:
        colorCode = FOREGROUND_BLUE;
        break;
    case ColorType::DarkGreen:
        colorCode = FOREGROUND_GREEN;
        break;
    case ColorType::DarkAqua:
        colorCode = FOREGROUND_GREEN | FOREGROUND_BLUE;
        break;
    case ColorType::DarkRed:
        colorCode = FOREGROUND_RED;
        break;
    case ColorType::DarkMagenta:
        colorCode = FOREGROUND_RED | FOREGROUND_BLUE;
        break;
    case ColorType::Yellow:
        colorCode = FOREGROUND_RED | FOREGROUND_GREEN;
        break;
    case ColorType::Gray:
        colorCode = FOREGROUND_INTENSITY;
        break;
    case ColorType::DarkGray:
        colorCode = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        break;
    case ColorType::Blue:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_BLUE;
        break;
    case ColorType::Green:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_GREEN;
        break;
    case ColorType::Aqua:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE;
        break;
    case ColorType::Red:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_RED;
        break;
    case ColorType::Magenta:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE;
        break;
    case ColorType::LightYellow:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN;
        break;
    case ColorType::White:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        break;
    default:
        colorCode = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        break;
    }

    SetConsoleTextAttribute(hConsole, colorCode);
}

4. Java部分

说明

没什么要注意的,没有多余的东西,只是用法和上面一样

直接复制粘贴就可以用,所以直接上代码:

代码

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.*;
import java.util.Base64;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public class Main {
    private static final String ResourcesPath = "D:\\Resources\\";
    private static final int MaxConcurrentRequests = 100;
    private static int Port = 1012;
    private static boolean IsRootPath = false;
    private static final int[] UnSafePorts = { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669 };
    private static final ConcurrentMap<String, byte[]> ResourceCache = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        if (args.length > 0) {
            try {
                Port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                // 看情况写
            }
        }
        if (Port == 0) {
            Port++;
        }
        if (Port == 65535) {
            Port = 1;
        }
        for (int unsafePort : UnSafePorts) {
            if (Port == 0) {
                Port++;
            }
            if (Port == 65535) {
                Port = 1;
            }
            if (Port == unsafePort) {
                Port++;
            }
        }

        while (true) {
            System.out.println("Server Starting!");
            InetSocketAddress localEndPoint = new InetSocketAddress("0.0.0.0", Port);
            try (ServerSocket listener = new ServerSocket()) {
                listener.bind(localEndPoint);
                InetAddress localIPAddress = null;
                String hostName = InetAddress.getLocalHost().getHostName();
                InetAddress[] ipAddresses = InetAddress.getAllByName(hostName);
                for (InetAddress address : ipAddresses) {
                    if (address instanceof Inet4Address) {
                        localIPAddress = address;
                        break;
                    }
                }
                String localIp = localIPAddress != null ? localIPAddress.getHostAddress() : "unknown";
                System.out.println("IP: " + localIp);
                System.out.println("Server Started On Port " + Port + ".");

                Semaphore semaphore = new Semaphore(MaxConcurrentRequests);
                while (true) {
                    System.out.println("Waiting Clients...");
                    semaphore.acquire();
                    Socket client = listener.accept();
                    System.out.println("Client Connected. IP: " + client.getRemoteSocketAddress());
                    new Thread(() -> handleRequest(client, semaphore)).start();
                }
            } catch (IOException e) {
                Port++;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void handleRequest(Socket client, Semaphore semaphore) {
        try {
            InputStream requestStream = client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(requestStream));
            String requestLine = reader.readLine();

            if (requestLine != null) {
                String[] tokens = requestLine.split(" ");
                String method = tokens[0];
                String path = tokens[1];

                if ("GET".equals(method)) {
                    if ("/".equals(path)) {
                        IsRootPath = true;
                        System.out.println("Client Requested Root Path.");
                        String filesHtml = getFilesHtml();
                        byte[] contentBytes = filesHtml.getBytes(StandardCharsets.UTF_8);
                        sendResponse(client.getOutputStream(), contentBytes);
                    }
                    if (ResourceCache.containsKey(path)) {
                        sendResponse(client.getOutputStream(), ResourceCache.get(path));
                    } else {
                        byte[] resourceBytes = loadResource(path);
                        ResourceCache.put(path, resourceBytes);
                        sendResponse(client.getOutputStream(), resourceBytes);
                    }
                }
            }

            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    }

    private static String getFilesHtml() {
        StringBuilder website = new StringBuilder();
        website.append("<html>");
        website.append("<meta charset=\"UTF-8\">");
        website.append("<head><title>HXLyxx - Super File Server</title></head>");
        website.append("<body>");
        website.append("<h2>Server Root Directory</h2>");
        website.append("<ul>");

        String rootPath = Paths.get(ResourcesPath).toString();
        try {
            DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(rootPath));
            for (Path file : directoryStream) {
                String fileName = file.getFileName().toString();
                website.append("<li><a href=\"/" + fileName + "\">" + fileName + "</a></li>");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        website.append("</ul>");
        website.append("</body>");
        website.append("</html>");

        return website.toString();
    }

    private static byte[] loadResource(String relativePath) {
        String fullPath = Paths.get(ResourcesPath, relativePath.substring(1)).toString();

        System.out.println("Loading Resource..." + fullPath);

        byte[] content;
        try {
            if (Files.exists(Paths.get(fullPath))) {
                System.out.println(fullPath + " Ready To Transfer!");
                content = Files.readAllBytes(Paths.get(fullPath));
                IsRootPath = false;
            } else if (IsRootPath) {
                content = new byte[0];
                IsRootPath = false;
            } else {
                System.out.println(fullPath + " Does Not Exist!");
                String notFoundMessage = "<html><meta charset=\"UTF-8\"><head><title>404</title></head><body><h1>404 Not Found</h1><br><h2>" + relativePath + "</h2></body></html>";
                content = notFoundMessage.getBytes(StandardCharsets.UTF_8);
                IsRootPath = false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            content = new byte[0];
        }

        return content;
    }

    private static void sendResponse(OutputStream outputStream, byte[] resourceBytes) {
        String eTag = calculateEtag(resourceBytes);

        System.out.println("Sending Response...");
        System.out.println("ETag: " + eTag);

        try {
            PrintWriter writer = new PrintWriter(outputStream, true);
            writer.println("HTTP/1.1 200 OK");
            writer.println();
            writer.flush();

            outputStream.write(resourceBytes);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String calculateEtag(byte[] data) {
        try {
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
            byte[] hash = sha256.digest(data);
            return Base64.getEncoder().encodeToString(hash);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
    }
}

5. 结语

网上的文件服务器都不是很好用,我就写了个好点的 :D

主要是我写了三遍啊三遍!!!

可能有点不完善,还请多多包涵awa

Made By HXLyxx

2023.8.23

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值