Перепишите ваш демон с использованием сокета. То есть теперь все общение идет через сокет с использование утилиты socat
. Отслеживать изменение файла либо писать в файл больше не надо. В качестве аргументов командной строки добавьте возможность указать либо путь к сокет-файлу, либо порт
Демоны / Работа с сокетам
Сейчас у нас общение с демоном осуществляется через файл, что не очень удобно как с точки зрения пользователя, который вынужден обновлять файл, чтобы увидеть ответ. Так и с точки зрения кода, где у нас появляются всякие костыли.
К счастью существует более эффективный способ общения с юзером. С помощью так называемых сокетов.
Согласно википедии, сокет – это программный интерфейс для обеспечения обмена данными между процессами. Причем процессы необязательно должны находится на одном и том же компьютере.
Если упростить, то сокет — это некий канал, который создает демон и к которому может подключится кто-нибудь из вне, начать отправлять в него запросы, а демон в ответ в этот же канал будет кидать ответы.
Самый яркий пример сокет соединения — это взаимодействие браузера с сайтом. Когда вы открываете страницу в бразуере, то создается подключение к сокету на удаленном сервере в который отправляется запрос и сервер в этот сокет кидает ответ, в результате данных процедур пользователь видет страницу сайта с текстом стилями, скриптами и прочей нечестью. Как правило для внешних соединений в качестве сокета используют комбинацию ip адреса с портом, но в тоже время сокет может представлять собой просто файлик, что идеально подходит для внутренних соединений. Собственно, нам такой вариант и подойдет.
Есть разные протоколы взаимодействия с сервером. Мы будем использовать протокол TCP/IP, потому что данный протокол предполагает создание диалога между пользователем (клиентом) и демоном (сервером), что идеально подходит под нашу ситуацию. Можно теперь забыть про наш старый код с FileSystemWatcher-ом и циклом с Thread.Sleep. Теперь у нас будет нормальный сервер который сам будет ждать соединения, в общем, пишем:
using System;
using System.Net.Sockets;
using System.Text;
namespace App
{
class Program {
public static void Main(String[] args) {
// проверяем не создали ли мы сокет файл уже
// и если создали то удаляем старый файл
if (File.Exists("daemon.socket")) {
File.Delete("daemon.socket");
}
// собственно создаем канал для сокета
UnixDomainSocketEndPoint endPoint = new("daemon.socket");
// создаем объекта сокета
var socket = new Socket(
endPoint.AddressFamily,
SocketType.Stream,
ProtocolType.IP
);
// подключаем к каналу
socket.Bind(endPoint);
// и начинаем слушать соединения
socket.Listen();
while(true) {
Console.WriteLine("Жду приказов");
var handler = socket.Accept(); // эта строка блокирует код пока кто-нибудь не подключится к сокету
// как только кто-то подключился мы можем начать обрабатывать команды от юзера
while (true) {
var bytes = new Byte[1024]; // буфер под введенные данные
int bytesRec = handler.Receive(bytes); // эта штука по сути аналог Console.ReadLine()
var input = Encoding.UTF8.GetString(bytes); // только полученные байты превращаем в строку
var output = $"Вы написали {input}"; // формируем ответ
handler.Send(Encoding.UTF8.GetBytes(output)); // посылаем ответ, преобразуя его обратно в байты
}
}
}
}
}
попробуем запустить
dotnet run
если глянуть, то увидим, что в папке появился файлик daemon.socket
причем обратите внимание что у него в списке флагов файлов первая буква s
, это означает что это не простой файл, а сокет-файл. То есть его нельзя просто так открыть или чего-то записать в него, он используется для организации канала общения между процессами.
Можно попробовать туда чего-нибудь echoнуть
правда оно не сработает так как это не настоящий файл.
В общем чтобы к нему подключится нам потребуется специальная программа для подключения к сокетам. Называется она socat
. Ставим ее себе
sudo apt install socat
Теперь попробуем подключится к серверу. Пишем
socat UNIX-CLIENT:daemon.socket -
и попробуем чего-нибудь пописать
чтобы остановить общение надо нажать Ctrl+C
это разорвет соединения socat, и заодно убьет наш сервер так для него такая ситуация является нештатной, что конечно такое себе.
В общем надо ловить эту ошибку и в случае ее возникновения прерывать соединения корректно вот так
// ...
while(true) {
// тут не трогаем
Console.WriteLine("Жду приказов");
var handler = socket.Accept();
// а цикл правим
while (true) {
try { // обернул в try
// тут старый код который раньше был в while
var bytes = new Byte[1024];
int bytesRec = handler.Receive(bytes);
var input = Encoding.UTF8.GetString(bytes);
var output = $"Вы написали {input}";
handler.Send(Encoding.UTF8.GetBytes(output));
} catch (SocketException) { // ловлю ошибку
// в случае ошибки пишу об этом
Console.WriteLine("Произошёл разрыв соединения");
// закрываю соединения аккуратно
handler.Shutdown(SocketShutdown.Both);
handler.Close();
break; // и выхожу из цикла перехвата ввода
}
}
}
// ...
Пробуем запустить
Отлично!
Так как нам в будущем потребуется сделать наш сервер доступным для внешних соединений. То важно уметь сконфигурировать сервер так, чтобы он слушал соединения не только через сокет-файл но и через сетевой порт.
Делается это так
using System;
using System.Net; // не забыть добавть using
using System.Net.Sockets;
using System.Text;
namespace App
{
class Program {
public static void Main(String[] args) {
// узнаем системный IP
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
// создаем точку подключения
IPEndPoint endPoint = new IPEndPoint(ipAddress, 8080);
Console.WriteLine($"Слушаю запросы на {ipAddress}:8080");
// ну и дальше как обычно
var socket = new Socket(
endPoint.AddressFamily,
SocketType.Stream,
ProtocolType.IP
);
socket.Bind(endPoint);
socket.Listen();
//...
}
}
}
И теперь если захочется подключится к серверу просто пишем вот так:
socat TCP:127.0.1.1:8080 -
вот теперь другое дело, можно пилить задачу