Создавая сетевое приложение, особенно игровое, часто возникает желательность автоматического подключения приложений друг к другу.
Часто в описании подключения сетевых программ требуется ввод сетевых координат: IP-адреса и номера порта сервера. Непродвинутым пользователям (чайникам 😊) сложно разбираться в IP адресах, номерах портов и их поисках на собственных компьютерах.
Для пользователей, и не только чайникам, было бы очень комфортно при наличии возможности самостоятельного подключения сетевых программ друг к другу. Какой сетевой протокол TCP или UDP использовать для таких программ и как построить алгоритм работы автоматического подключения по сети? Один из ответов на этот вопрос описан в исходнике прикрепленного приложения-чата.
Широковещательные (broadcast) адреса
Широковещательный адрес (broadcast) – это зарезервированный условный IP-адрес, предназначенный для отправки сообщений всем участникам сети. Широковещательные адреса для разных подсетей имеют виды - 255.255.255.255, 192.255.255.255, 192.168.255.255, 192.168.1.255. Соответственно широковещательный адрес 255.255.255.255 позволяет отправлять сообщений клиентам всех сетей, 192.168.1.255 только клиентам сети 192.168.1.0.
Широковещательные адреса вычисляются на основе маски подсети и IP-адреса устройства. Например: маска подсети 255.255.0.0 и адрес устройства 172.25.14.23 создают широковещательный адрес 172.25.255.255 для сети 172.25.0.0, маска 255.255.255.0 и IP-адрес 192.168.0.34 создают broadcast-адрес 192.168.0.255 для сети 192.168.0.0. Отправка широковещательных сообщений не предполагает получения обратных ответов.
Кратко о работе TCP
Протокол TCP обеспечивает надежную передачу данных, гарантируя неизменную последовательность и целостность отправленных данных. TCP построен на принципе соединения и обеспечивает связь между двумя точками только после трехэтапного рукопожатия: клиент серверу отправляет запрос на коннект, сервер отвечает клиенту подтверждением, клиент получает подтверждение и после этого соединение считается успешно установленным.
Создавая сетевые приложения, работающие по протоколу TCP для соединения необходимо предварительное точное знание IP-адреса и номера порта сервера. TCP не допускает отправку сообщений на широковещательные (broadcast) адреса из-за необходимости предварительного подключения.
Кратко о работе UDP
Организация связи по протоколу UDP не требует факта установления предварительного соединения. Клиент UDP может отправлять сообщения по определенному адресу и номеру порта, даже не имея информации о существовании сервера. UDP не гарантирует доставку и последовательность сообщений, но гарантирует целостность полученного сообщения.
UDP поддерживает отправку широковещательных сообщений всем потенциальным клиентам сети. Данное свойство протокола UDP можно использовать для поиска и организации связи между сетевыми программами.
Алгоритм автоматического подключения по сети
Используя теоретические знания о свойствах протоколов TCP, UDP и широковещательных адресов можно построить алгоритм автоматического подключения сетевых программ друг к другу. Для этого лучше использовать «команду» протоколов: UDP – для поиска клиентов в сети, TCP – для установления надежного соединения между сетевыми приложениями. Чтобы не засорять локальную сеть, широковещательные адреса отсылать разово, по действию пользователя.
Вычисление широковещательного адреса на основе маски подсети и IP-адреса устройства.
Поиск сервера в сети, если сервер не обнаружен, то создается сервер для возможных подключений.
Если сервер обнаружен, то приложение подключается к нему как клиент.
С найденными родственными приложениями создается канал обмена сообщениями по протоколу TCP.
Сетевой чат с автоматическим подключением
На основе вышеописанного алгоритма создано сетевое приложение-чат на C# Windows Forms с автоматическим подключением друг к другу. После авто-подключения чат начинает работать и можно обмениваться сообщениями с клиентами.
Процесс подключения к возможным клиентам в сети активируется после нажатия кнопки Подключение и если клиентов в сети нет, то создается сервер для прослушивания подключений. При наличии сервера в сети приложение-чат подключается к серверу.
Экземпляры сетевого приложения имеют совмещённую функциональность сервера и клиента, соответствующий режим работы устанавливается при автоматическом подключении. Для идентификации сообщений у каждого клиента есть имя. Количество подключаемых клиентов по локальной сети ограничено ресурсами машины, на которой хостится сервер. Приложение поставляется с исходным кодом и можно изменить интерфейс по своему желанию.
Кнопка Служебные данные показывает окно со списком последовательности значимых действий сетевого приложения. Служебные данные пригодятся при модификации и отладки приложения.
Состав классов сетевого чата с авто-подключением
Исходник сетевого чата с авто-подключением состоит из двух форм и нескольких вспомогательных классов.
FormMain – основной класс-форма, визуальный интерфейс управления чатом.
FormServiceData – класс-форма для вывода служебных данных: действий, производимых сервисами UDP и TCP. Необходима для визуализации последовательности действий.
UDPModule – UDP сервис поиска сервера и клиентов в сети.
BytesPack – класс сериализации объекта в массив байтов и обратно.
Common – класс общих данных приложения
DataClient – класс данных клиента, служит для обмена информацией между клиентами, рассылаемой модулем UDP.
DataServer – класс хранения IP и номера порта сервера TCP.
IPMacnine и IPAddressExtensions – классы для вычисления широковещательного адреса сети текущей машины.
MyList – класс производный от типизированного списка List, расширяет стандартный список событием изменения списка при удалении и добавлении элементов.
Utils – вспомогательный класс статических методов, обеспечивающих безопасное управление интерфейсными элементами форм из других потоков.
Модуль UDP сервиса
UDPModule – функциональный сетевой модуль, работающий по протоколу UDP для поиска сервера и клиентов в сети, принадлежащих только данному сетевому приложению. Класс UDPModule обеспечивает надежность работы обмена датаграммами в случае занятости рабочих портов сторонними приложениями.
Работа по протоколу UDP выделена в отдельный класс и общение с главной формой происходит посредством событий. Например, при обнаружении в сети сервера для клиентов чата генерируется соответствующее событие и данные передаются в обработчик события сервиса TCP. Связь с другими классами, создаваемая на основе событий, упрощает внедрение программного кода UDPModule в любое сетевое приложение, где требуется автоматическое подключение по сети.
Краткий листинг программного кода модуля UDPModule:
internal class UDPModule
{
#region Определение полей и свойств
// Определение делегата для события получения данных сервера.
public delegate void ReceivedServerDataEventHandler(DataServer serverTCP);
public event ReceivedServerDataEventHandler? ReceivedServerData;
// Определение делегата для события изменения состояния приложения.
public delegate void StatusEventHandler(string status);
public event StatusEventHandler? Status;
// Определение делегата для события изменения режима работы приложения.
public delegate void ModeAppEventHandler(int mode);
public event ModeAppEventHandler? ModeApplication;
// Хранение UDP данных родственных приложений.
public List _clientsUDP = new();
...
#endregion
public UDPModule(FormServiceData serviceData, int idClient)
{
_serviceData = serviceData;
// Получение идентификатора клиента.
_dataClient.id = idClient;
Utils.TextToLabels(_serviceData.lblClientID, _dataClient.id.ToString());
// Сохранение идентификатора приложения в
// данных клиента для обмена по сети.
_dataClient.idApplication = Common.IdApplication;
}
#region Инициализация UDP сервиса
// Иициализация сервиса обмена широковещательными
// сообщениями по протоколоу UDP в пределах одноранговой сети.
public bool InitUDP()
{
...
// Чтобы основной поток приложения не блокировался,
// для извлечения сообщений запускаем дополнительный поток.
ThreadStart tstart = new(ReceiveUDP);
Thread threadReceiveUDP = new(tstart)
{
// Делаем поток фоновым, чтобы при закрытии приложения
// он автоматически прекращал свою работу.
IsBackground = true
};
threadReceiveUDP.Start();
// Успешная инициализация UDP сервиса.
return true;
}
#endregion
#region Получение UDP-датаграмм
// Прием датаграмм UDP.
private void ReceiveUDP()
{
//
while (_udp != null)
{
IPEndPoint? remote = null;
byte[] bytes = _udp.Receive(ref remote);
// Проверка родственного приложения,
// если прошел проверку включаем в группу
// и отвечаем ему.
// Свои сообщения игнорируем.
if (BytesPack.FromBytes(bytes) is DataClient recvDataClient &&
recvDataClient.idApplication == Common.IdApplication &&
recvDataClient.id != _dataClient.id)
{
// Заполняем и обновляем список клинтов.
AddListClientsUDP(recvDataClient);
// Обработка полученной датаграммы
switch (recvDataClient.typeMessage)
{
...
}
}
}
}
#endregion
#region Вспомогательные методы для UDP-сервиса
// Добавляем нового клиента в списко клиентов UDP.
void AddListClientsUDP(DataClient dataClient)
{
// Если такой клиент уже есть в списке, то обновляем его данные.
DataClient? p = _clientsUDP.FirstOrDefault(p => p.id == dataClient.id);
if (p != null)
{
_clientsUDP.Remove(p);
}
_clientsUDP.Add(dataClient);
...
}
public bool ServerSearch()
{
// Подготавливаем широковещательный адрес текущей машины.
IPAddress thisBroadcast = IPMacnine.GetBroadcastIP();
DataClient dataClient = _dataClient;
// Указываем тип сообщения - поиск сервера.
dataClient.typeMessage = TypeMessage.ServerSearch;
// Преобразуем объект в байты.
byte[] bytes = BytesPack.ToBytes(dataClient);
// Отправка сообщения на все номера портов, поскольку
// есть вероятность занятости первого порта в списке.
for (int i = 0; i < Common.Ports.Length; i++)
{
// Пример: широковещательный адрес для маски подсети 255.255.255.0
// IPEndPoint ep = new(IPAddress.Parse("192.168.1.255"), номер порта);
IPEndPoint ep = new(thisBroadcast, Common.Ports[i]);
SendDatagramUDP(bytes, ep);
}
return _sentFirstRequestServer;
}
// Ответ отправляется только клиенту запросившему сервер.
void ResponseServerSearch(string ip, int port)
{
DataClient dataClient = _dataClient;
// Указываем соответствующий тип сообщения.
dataClient.typeMessage = TypeMessage.ResponseServerSearch;
byte[] bytes = BytesPack.ToBytes(dataClient);
IPEndPoint ep = new(IPAddress.Parse(ip), port);
SendDatagramUDP(bytes, ep);
}
public static void SendDatagramUDP(byte[] bytes, IPEndPoint ep)
{
using UdpClient udp = new();
udp.Connect(ep);
udp.Send(bytes, bytes.Length);
}
// Определение режима работы приложения: сервер или клиент.
public bool DoServer()
{
// Определения сервера среди ответов других клиентов.
bool doServer = !_clientsUDP.Where(e => e.id != _dataClient.id && e.mode == 1).Any();
// Указание созлавать сервер, в противном случае клиента.
if (doServer == true)
{
...
}
else
{
...
}
// Генерация события изменения статуса приложения.
OnModeApplication(_dataClient.mode);
return doServer;
}
#endregion
#region Методы вызова событий генерируемых сервисом UDP
protected virtual void OnReceivedServerData(DataServer serverTCP)
{
ReceivedServerData?.Invoke(serverTCP);
}
protected virtual void OnStatus(string status)
{
Status?.Invoke(status);
}
protected virtual void OnModeApplication(int mode)
{
ModeApplication?.Invoke(mode);
}
#endregion
}
Исходный код сетевого чата с автоматическим подключением по сети
Исходник написан на языке C# в MS Visual Studio 2022, приложение Windows Forms .NET6. Исходный код подробно комментирован, описан общий смысл работы методов, свойств и полей классов. Функциональность автоматического подключения приложения-чата можно легко перенести на другую сетевую игру или прикладную программу.
Сразу после оплаты активируется ссылка на скачивание исходника. Также, на указанный e-mail высылается код доступа. Код действует 2 суток. Внимание: письмо может попасть в папку спама.
Скачать исходник
Тема: «Автоматическое подключение по локальной сети»💾WFAutoConnect-vs17.zipРазмер:123 КбайтЗагрузки:12