/******************************************************************************
Online C# Compiler.
Code, Compile, Run and Debug C# program online.
Write your code in this editor and press "Run" button to execute it.
*******************************************************************************/
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
class HelloWorld {
static void Main() {
Console.WriteLine("Hello World");
Program.Main(null);
}
}
public class SimpleWebServer
{
private readonly HttpListener _listener;
private readonly string _staticFilesPath;
private readonly Dictionary<string, string> _mimeTypes; // Для сопоставления расширений с MIME-типами
public SimpleWebServer(string[] prefixes, string staticFilesPath)
{
if (!HttpListener.IsSupported)
{
throw new NotSupportedException("HttpListener is not supported on this platform.");
}
_listener = new HttpListener();
foreach (string prefix in prefixes)
{
_listener.Prefixes.Add(prefix);
}
_staticFilesPath = staticFilesPath;
if (!Directory.Exists(_staticFilesPath))
{
Directory.CreateDirectory(_staticFilesPath); // Создаем папку, если ее нет
}
_mimeTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".css", "text/css" },
{ ".js", "application/javascript" },
{ ".json", "application/json" },
{ ".png", "image/png" },
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".gif", "image/gif" },
{ ".ico", "image/x-icon" },
{ ".svg", "image/svg+xml" },
{ ".txt", "text/plain" },
// Добавьте другие MIME-типы по мере необходимости
};
}
public async Task Start()
{
_listener.Start();
Console.WriteLine($"Server started. Listening on: {string.Join(", ", _listener.Prefixes)}");
Console.WriteLine($"Serving static files from: {_staticFilesPath}");
while (_listener.IsListening)
{
try
{
HttpListenerContext context = await _listener.GetContextAsync();
_ = HandleRequestAsync(context); // Обрабатываем запрос асинхронно, не дожидаясь завершения
}
catch (HttpListenerException ex)
{
Console.WriteLine($"HttpListenerException: {ex.Message}");
// Может возникнуть, если слушатель остановлен или произошла другая ошибка.
// В реальном приложении здесь можно добавить логику повторного запуска или выхода.
break;
}
catch (ObjectDisposedException)
{
// Listener был остановлен, выходим из цикла
break;
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}
}
Console.WriteLine("Server stopped.");
}
public void Stop()
{
if (_listener.IsListening)
{
_listener.Stop();
_listener.Close();
}
}
private async Task HandleRequestAsync(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Received request: {request.Url.AbsolutePath}");
try
{
if (request.HttpMethod == "GET")
{
string path = request.Url.AbsolutePath;
// 2. Обработка запросов к оборудованию
if (path.StartsWith("/api/device/", StringComparison.OrdinalIgnoreCase))
{
await HandleDeviceRequest(request, response, path.Substring("/api/device/".Length));
}
// 1. Выдача HTML-страниц, изображений, CSS и т.д.
else
{
await ServeStaticFile(request, response);
}
}
else
{
// Неподдерживаемые методы HTTP
await SendErrorResponse(response, HttpStatusCode.MethodNotAllowed, "Method Not Allowed");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error handling request {request.Url.AbsolutePath}: {ex.Message}");
await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Internal Server Error");
}
finally
{
response.Close(); // Всегда закрываем поток ответа
}
}
private async Task ServeStaticFile(HttpListenerRequest request, HttpListenerResponse response)
{
string filePath = request.Url.AbsolutePath;
if (filePath == "/")
{
filePath = "/index.html"; // По умолчанию обслуживаем index.html
}
string fullPath = Path.Combine(_staticFilesPath, filePath.TrimStart('/'));
if (File.Exists(fullPath))
{
try
{
string extension = Path.GetExtension(fullPath);
if (_mimeTypes.TryGetValue(extension, out string mimeType))
{
response.ContentType = mimeType;
}
else
{
response.ContentType = "application/octet-stream"; // Тип по умолчанию для неизвестных файлов
}
byte[] fileBytes = await File.ReadAllBytesAsync(fullPath);
response.ContentLength64 = fileBytes.Length;
await response.OutputStream.WriteAsync(fileBytes, 0, fileBytes.Length);
response.StatusCode = (int)HttpStatusCode.OK;
}
catch (FileNotFoundException) // Дополнительная проверка на всякий случай
{
await SendErrorResponse(response, HttpStatusCode.NotFound, "File not found.");
}
catch (UnauthorizedAccessException)
{
await SendErrorResponse(response, HttpStatusCode.Forbidden, "Access to the file is forbidden.");
}
catch (Exception ex)
{
Console.WriteLine($"Error serving static file {fullPath}: {ex.Message}");
await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Error serving file.");
}
}
else
{
await SendErrorResponse(response, HttpStatusCode.NotFound, "File not found.");
}
}
private async Task HandleDeviceRequest(HttpListenerRequest request, HttpListenerResponse response, string deviceCommand)
{
try
{
// Здесь будет ваша логика взаимодействия с оборудованием
// deviceCommand будет содержать часть URL после "/api/device/",
// например, "status" или "set_value?param=10"
Console.WriteLine($"Handling device command: {deviceCommand}");
string responseContent = "{}"; // Значение по умолчанию
HttpStatusCode statusCode = HttpStatusCode.OK;
switch (deviceCommand.ToLower())
{
case "status":
// Пример: получить статус от оборудования
responseContent = await GetDeviceStatusAsync();
break;
case "set_value":
// Пример: установить значение на оборудовании
string paramValue = request.QueryString["param"];
if (!string.IsNullOrEmpty(paramValue) && int.TryParse(paramValue, out int value))
{
bool success = await SetDeviceValueAsync(value);
responseContent = success ? "{\"status\": \"ok\", \"message\": \"Value set.\"}" : "{\"status\": \"error\", \"message\": \"Failed to set value.\"}";
}
else
{
statusCode = HttpStatusCode.BadRequest;
responseContent = "{\"status\": \"error\", \"message\": \"Invalid 'param' value.\"}";
}
break;
default:
statusCode = HttpStatusCode.NotFound;
responseContent = "{\"status\": \"error\", \"message\": \"Unknown device command.\"}";
break;
}
response.ContentType = "application/json";
byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
response.ContentLength64 = buffer.Length;
response.StatusCode = (int)statusCode;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.WriteLine($"Error processing device request '{deviceCommand}': {ex.Message}");
await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Error processing device request.");
}
}
// --- Методы-заглушки для работы с оборудованием ---
private async Task<string> GetDeviceStatusAsync()
{
// Здесь должна быть реальная логика запроса к оборудованию
// Например, асинхронный вызов по TCP/IP, Modbus, UART и т.д.
await Task.Delay(100); // Имитация задержки
return "{\"deviceStatus\": \"online\", \"temperature\": 25.5, \"humidity\": 60}";
}
private async Task<bool> SetDeviceValueAsync(int value)
{
// Здесь должна быть реальная логика отправки команды оборудованию
await Task.Delay(50); // Имитация задержки
Console.WriteLine($"Setting device value to: {value}");
return true; // Возвращаем true, если операция успешна
}
// --- Конец методов-заглушек ---
private async Task SendErrorResponse(HttpListenerResponse response, HttpStatusCode statusCode, string message)
{
response.StatusCode = (int)statusCode;
response.ContentType = "text/plain";
byte[] buffer = Encoding.UTF8.GetBytes(message);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
}
public class Program
{
public static async Task Main(string[] args)
{
string staticFilesDir = Path.Combine(AppContext.BaseDirectory, "wwwroot");
string[] prefixes = { "http://localhost:8080/", "http://127.0.0.1:8080/" };
SimpleWebServer server = new SimpleWebServer(prefixes, staticFilesDir);
// Для запуска сервера и ожидания его работы
var serverTask = server.Start();
Console.WriteLine("Press 'q' to quit the server.");
while (Console.ReadKey(true).KeyChar != 'q')
{
// Ждем нажатия 'q'
}
server.Stop();
await serverTask; // Ждем завершения задачи сервера, чтобы убедиться, что все контексты закрыты
}
}