fork download
  1. /******************************************************************************
  2.  
  3.   Online C# Compiler.
  4.   Code, Compile, Run and Debug C# program online.
  5. Write your code in this editor and press "Run" button to execute it.
  6.  
  7. *******************************************************************************/
  8.  
  9. using System;
  10. using System.IO;
  11. using System.Net;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using System.Collections.Generic;
  15.  
  16.  
  17. class HelloWorld {
  18. static void Main() {
  19. Console.WriteLine("Hello World");
  20. Program.Main(null);
  21.  
  22. }
  23. }
  24.  
  25.  
  26.  
  27. public class SimpleWebServer
  28. {
  29. private readonly HttpListener _listener;
  30. private readonly string _staticFilesPath;
  31. private readonly Dictionary<string, string> _mimeTypes; // Для сопоставления расширений с MIME-типами
  32.  
  33. public SimpleWebServer(string[] prefixes, string staticFilesPath)
  34. {
  35. if (!HttpListener.IsSupported)
  36. {
  37. throw new NotSupportedException("HttpListener is not supported on this platform.");
  38. }
  39.  
  40. _listener = new HttpListener();
  41. foreach (string prefix in prefixes)
  42. {
  43. _listener.Prefixes.Add(prefix);
  44. }
  45.  
  46. _staticFilesPath = staticFilesPath;
  47. if (!Directory.Exists(_staticFilesPath))
  48. {
  49. Directory.CreateDirectory(_staticFilesPath); // Создаем папку, если ее нет
  50. }
  51.  
  52. _mimeTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  53. {
  54. { ".html", "text/html" },
  55. { ".htm", "text/html" },
  56. { ".css", "text/css" },
  57. { ".js", "application/javascript" },
  58. { ".json", "application/json" },
  59. { ".png", "image/png" },
  60. { ".jpg", "image/jpeg" },
  61. { ".jpeg", "image/jpeg" },
  62. { ".gif", "image/gif" },
  63. { ".ico", "image/x-icon" },
  64. { ".svg", "image/svg+xml" },
  65. { ".txt", "text/plain" },
  66. // Добавьте другие MIME-типы по мере необходимости
  67. };
  68. }
  69.  
  70. public async Task Start()
  71. {
  72. _listener.Start();
  73. Console.WriteLine($"Server started. Listening on: {string.Join(", ", _listener.Prefixes)}");
  74. Console.WriteLine($"Serving static files from: {_staticFilesPath}");
  75.  
  76. while (_listener.IsListening)
  77. {
  78. try
  79. {
  80. HttpListenerContext context = await _listener.GetContextAsync();
  81. _ = HandleRequestAsync(context); // Обрабатываем запрос асинхронно, не дожидаясь завершения
  82. }
  83. catch (HttpListenerException ex)
  84. {
  85. Console.WriteLine($"HttpListenerException: {ex.Message}");
  86. // Может возникнуть, если слушатель остановлен или произошла другая ошибка.
  87. // В реальном приложении здесь можно добавить логику повторного запуска или выхода.
  88. break;
  89. }
  90. catch (ObjectDisposedException)
  91. {
  92. // Listener был остановлен, выходим из цикла
  93. break;
  94. }
  95. catch (Exception ex)
  96. {
  97. Console.WriteLine($"An unexpected error occurred: {ex.Message}");
  98. }
  99. }
  100.  
  101. Console.WriteLine("Server stopped.");
  102. }
  103.  
  104. public void Stop()
  105. {
  106. if (_listener.IsListening)
  107. {
  108. _listener.Stop();
  109. _listener.Close();
  110. }
  111. }
  112.  
  113. private async Task HandleRequestAsync(HttpListenerContext context)
  114. {
  115. HttpListenerRequest request = context.Request;
  116. HttpListenerResponse response = context.Response;
  117.  
  118. Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Received request: {request.Url.AbsolutePath}");
  119.  
  120. try
  121. {
  122. if (request.HttpMethod == "GET")
  123. {
  124. string path = request.Url.AbsolutePath;
  125.  
  126. // 2. Обработка запросов к оборудованию
  127. if (path.StartsWith("/api/device/", StringComparison.OrdinalIgnoreCase))
  128. {
  129. await HandleDeviceRequest(request, response, path.Substring("/api/device/".Length));
  130. }
  131. // 1. Выдача HTML-страниц, изображений, CSS и т.д.
  132. else
  133. {
  134. await ServeStaticFile(request, response);
  135. }
  136. }
  137. else
  138. {
  139. // Неподдерживаемые методы HTTP
  140. await SendErrorResponse(response, HttpStatusCode.MethodNotAllowed, "Method Not Allowed");
  141. }
  142. }
  143. catch (Exception ex)
  144. {
  145. Console.WriteLine($"Error handling request {request.Url.AbsolutePath}: {ex.Message}");
  146. await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Internal Server Error");
  147. }
  148. finally
  149. {
  150. response.Close(); // Всегда закрываем поток ответа
  151. }
  152. }
  153.  
  154. private async Task ServeStaticFile(HttpListenerRequest request, HttpListenerResponse response)
  155. {
  156. string filePath = request.Url.AbsolutePath;
  157. if (filePath == "/")
  158. {
  159. filePath = "/index.html"; // По умолчанию обслуживаем index.html
  160. }
  161.  
  162. string fullPath = Path.Combine(_staticFilesPath, filePath.TrimStart('/'));
  163.  
  164. if (File.Exists(fullPath))
  165. {
  166. try
  167. {
  168. string extension = Path.GetExtension(fullPath);
  169. if (_mimeTypes.TryGetValue(extension, out string mimeType))
  170. {
  171. response.ContentType = mimeType;
  172. }
  173. else
  174. {
  175. response.ContentType = "application/octet-stream"; // Тип по умолчанию для неизвестных файлов
  176. }
  177.  
  178. byte[] fileBytes = await File.ReadAllBytesAsync(fullPath);
  179. response.ContentLength64 = fileBytes.Length;
  180. await response.OutputStream.WriteAsync(fileBytes, 0, fileBytes.Length);
  181. response.StatusCode = (int)HttpStatusCode.OK;
  182. }
  183. catch (FileNotFoundException) // Дополнительная проверка на всякий случай
  184. {
  185. await SendErrorResponse(response, HttpStatusCode.NotFound, "File not found.");
  186. }
  187. catch (UnauthorizedAccessException)
  188. {
  189. await SendErrorResponse(response, HttpStatusCode.Forbidden, "Access to the file is forbidden.");
  190. }
  191. catch (Exception ex)
  192. {
  193. Console.WriteLine($"Error serving static file {fullPath}: {ex.Message}");
  194. await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Error serving file.");
  195. }
  196. }
  197. else
  198. {
  199. await SendErrorResponse(response, HttpStatusCode.NotFound, "File not found.");
  200. }
  201. }
  202.  
  203. private async Task HandleDeviceRequest(HttpListenerRequest request, HttpListenerResponse response, string deviceCommand)
  204. {
  205. try
  206. {
  207. // Здесь будет ваша логика взаимодействия с оборудованием
  208. // deviceCommand будет содержать часть URL после "/api/device/",
  209. // например, "status" или "set_value?param=10"
  210. Console.WriteLine($"Handling device command: {deviceCommand}");
  211.  
  212. string responseContent = "{}"; // Значение по умолчанию
  213. HttpStatusCode statusCode = HttpStatusCode.OK;
  214.  
  215. switch (deviceCommand.ToLower())
  216. {
  217. case "status":
  218. // Пример: получить статус от оборудования
  219. responseContent = await GetDeviceStatusAsync();
  220. break;
  221. case "set_value":
  222. // Пример: установить значение на оборудовании
  223. string paramValue = request.QueryString["param"];
  224. if (!string.IsNullOrEmpty(paramValue) && int.TryParse(paramValue, out int value))
  225. {
  226. bool success = await SetDeviceValueAsync(value);
  227. responseContent = success ? "{\"status\": \"ok\", \"message\": \"Value set.\"}" : "{\"status\": \"error\", \"message\": \"Failed to set value.\"}";
  228. }
  229. else
  230. {
  231. statusCode = HttpStatusCode.BadRequest;
  232. responseContent = "{\"status\": \"error\", \"message\": \"Invalid 'param' value.\"}";
  233. }
  234. break;
  235. default:
  236. statusCode = HttpStatusCode.NotFound;
  237. responseContent = "{\"status\": \"error\", \"message\": \"Unknown device command.\"}";
  238. break;
  239. }
  240.  
  241. response.ContentType = "application/json";
  242. byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
  243. response.ContentLength64 = buffer.Length;
  244. response.StatusCode = (int)statusCode;
  245. await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
  246. }
  247. catch (Exception ex)
  248. {
  249. Console.WriteLine($"Error processing device request '{deviceCommand}': {ex.Message}");
  250. await SendErrorResponse(response, HttpStatusCode.InternalServerError, "Error processing device request.");
  251. }
  252. }
  253.  
  254. // --- Методы-заглушки для работы с оборудованием ---
  255. private async Task<string> GetDeviceStatusAsync()
  256. {
  257. // Здесь должна быть реальная логика запроса к оборудованию
  258. // Например, асинхронный вызов по TCP/IP, Modbus, UART и т.д.
  259. await Task.Delay(100); // Имитация задержки
  260. return "{\"deviceStatus\": \"online\", \"temperature\": 25.5, \"humidity\": 60}";
  261. }
  262.  
  263. private async Task<bool> SetDeviceValueAsync(int value)
  264. {
  265. // Здесь должна быть реальная логика отправки команды оборудованию
  266. await Task.Delay(50); // Имитация задержки
  267. Console.WriteLine($"Setting device value to: {value}");
  268. return true; // Возвращаем true, если операция успешна
  269. }
  270. // --- Конец методов-заглушек ---
  271.  
  272. private async Task SendErrorResponse(HttpListenerResponse response, HttpStatusCode statusCode, string message)
  273. {
  274. response.StatusCode = (int)statusCode;
  275. response.ContentType = "text/plain";
  276. byte[] buffer = Encoding.UTF8.GetBytes(message);
  277. response.ContentLength64 = buffer.Length;
  278. await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
  279. }
  280. }
  281.  
  282. public class Program
  283. {
  284. public static async Task Main(string[] args)
  285. {
  286. string staticFilesDir = Path.Combine(AppContext.BaseDirectory, "wwwroot");
  287. string[] prefixes = { "http://localhost:8080/", "http://127.0.0.1:8080/" };
  288.  
  289. SimpleWebServer server = new SimpleWebServer(prefixes, staticFilesDir);
  290.  
  291. // Для запуска сервера и ожидания его работы
  292. var serverTask = server.Start();
  293.  
  294. Console.WriteLine("Press 'q' to quit the server.");
  295. while (Console.ReadKey(true).KeyChar != 'q')
  296. {
  297. // Ждем нажатия 'q'
  298. }
  299.  
  300. server.Stop();
  301. await serverTask; // Ждем завершения задачи сервера, чтобы убедиться, что все контексты закрыты
  302. }
  303. }
Success #stdin #stdout 0.11s 36312KB
stdin
Standard input is empty
stdout
Hello World
Server started. Listening on: http://localhost:8080/, http://127.0.0.1:8080/
Serving static files from: /tmp/aMgluo/bin/Debug/net6.0/wwwroot
Press 'q' to quit the server.