目录
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