Работаем с сокетами в C# по UDP-протоколу
Для наглядности напишем два приложения, которые будут общаться друг с другом. Одно будет сервером, другое клиентом. Общаться они будут по UDP протоколу.
Клиентское приложение
Начнем с клиента… Создаем консольный проект. В проекте пишем две функции, одна из которых будет «слушать» и «отвечать» на запросы. Назовем ее Listen.
//Для клиента
publiс void Listen()
{
int recv; //храним размер полученных данных
byte data = new byte[1024]; //данные, которые будут передаваться или приниматься
//создаем новый сокет
//параметр AddressFamily задает схему адресации. В нашем случае это адреса IPv4
//параметр SocketType указывает, какой тип сокета применяется.
//В данном случае SocketType.Dgram это ненадежные сообщения, но для примера хватит.
//Тем более, что Dgram поддерживает протокол UDP
//последний параметр, ProtocolType, задает тип протокола
Socket mysocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//создаем конечную точку по адресу сокета. Т.е. будем "слушать" порт 9051 и контролировать все сетевые интерфейсы
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9051);
mysocket.Bind(ipep); //привязываем точку к нашему сокету
//создаем еще одну точку
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
//определяем сетевой адрес
EndPoint Remote = (EndPoint)(sender);
//отправляем первое сообщение нашему серверу
string text = "Hello"; //текст сообщения
data = Encoding.ASCII.GetBytes(text); //переводим строку в байты
//отправляем на указанны адрес
//первый параметр это сами данные в виде массива байт
//второй параметр, какая длиная сообщения должна быть передана.
//Если указать меньше, то передаст только то число символов, сколько укажете
//третий параметр указывает поведение сокета при приеме и получении данных. В нашем случае ничего...
//четвертый парамерт задает адрес и порт сервера, которому нужно отправить сообщение
//что делает функция _getHost, смотрите чуть ниже...
//в данном примере используется бродкастовый адрес подсети 192.168.15.255,
//если мы не знаем по какому адресу находится сервер. Порт подставляем любой (1111), он все равно будет перезаписан в функции _getHost
//Использование функции SendTo позволяет заранее не соединяться с сервером
mysocket.SendTo(data, data.Length, SocketFlags.None, _getHost("192.168.15.255:1111"));
//запускаем бесконечный цикл, который будет принимать и отправлять данные
while (true)
{
data = new byte[1024];
//принимаем данные от сервера. recv содержир размер, т.е. количество принятых символов
recv = mysocket.ReceiveFrom(data, ref Remote);
//в данном примере сервер шлет сообщение, которое разделено ":"
//разбираем его в массив
//функция Encoding.ASCII.GetString переводит массив байт в строку
string[] args = Encoding.ASCII.GetString(data, 0, recv).ToLower().Split(':');
foreach(string item in args)
{
Console.WriteLine("Сервер отправил " + item);
}
//отправим ответ что мы получили. Например, первый элемент массива
data = Encoding.ASCII.GetBytes(args[0]);
//Remote содержит адрес, с которого пришло сообщение. Ему его назад и отправляем
mysocket.SendTo(data, data.Length, SocketFlags.None, _getHost(Remote.ToString()));
}
}
//функция _getHost
private EndPoint _getHost(string text)
{
//вырезаем из строки только IP адрес формата IPv4
string host = text.Remove(text.IndexOf(":"), text.Length - text.IndexOf(":"));
//создаем объект адреса. Переменная host уже имеет вид [0-255].[0-255].[0-255].[0-255]
IPAddress hostIPAddress = IPAddress.Parse(host);
//создаем конечную точку. В нашем случае это адрес сервера, который слушает порт 9050
IPEndPoint hostIPEndPoint = new IPEndPoint(hostIPAddress, 9050);
EndPoint To = (EndPoint)(hostIPEndPoint);
return To;
}
Сервер
Приложение сервер также будет состоять из двух функций, но не будет отправлять запрос с приветствием. Просто будет слушать и в зависимости от принятых данных отправлять сообщения.
Внимание! Порт, который слушает сервер — 9050. Отправляет он на 9051. Это в нашем случае.
Создаем новое консольное приложение для сервера и смотрим код:
//Для сервера
publiс void Listen()
{
int recv; //храним размер полученных данных
byte data = new byte[1024]; //данные, которые будут передаваться или приниматься
Socket mysocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//В отличие от клиента "слушаем" порт 9050
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
mysocket.Bind(ipep); //привязываем точку к нашему сокету
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
//запускаем бесконечный цикл, который будет принимать и отправлять данные
while (true)
{
data = new byte[1024];
//принимаем данные от клиента. recv содержир размер, т.е. количество принятых символов
recv = mysocket.ReceiveFrom(data, ref Remote);
string message = Encoding.ASCII.GetString(data, 0, recv);
switch(message)
{
case "Hello" : //на ответ от клиента Hello, шлем ответ ОК
data = Encoding.ASCII.GetBytes("ОК:Сервер найден!");
break;
}
//Remote содержит адрес, с которого пришло сообщение. Ему его назад и отправляем
mysocket.SendTo(data, data.Length, SocketFlags.None, _getHost(Remote.ToString()));
}
}
//функция _getHost для сервера отличается только портом. Вместо 9050, отправлять всегда будем на 9051
private EndPoint _getHost(string text)
{
//вырезаем из строки только IP адрес формата IPv4
string host = text.Remove(text.IndexOf(":"), text.Length - text.IndexOf(":"));
//создаем объект адреса. Переменная host уже имеет вид [0-255].[0-255].[0-255].[0-255]
IPAddress hostIPAddress = IPAddress.Parse(host);
//создаем конечную точку. В нашем случае это адрес сервера, который слушает порт 9051
IPEndPoint hostIPEndPoint = new IPEndPoint(hostIPAddress, 9051);
EndPoint To = (EndPoint)(hostIPEndPoint);
return To;
}
Заключение
В статье описан самый простой и базовый пример работы с сокетами. Здесь главное — понять суть дела.
Популярность: 96%
Если у вас возникли вопросы, вы можете оставить их в комментариях


case: «Hello» : //на ответ от клиента Hello, шлем ответ ОК
У тебя в этой строке ошибка. Только вот какая — я понять не смог))
Методом научного тыка я понял что вот так будет без ошибки:
case «Hello»: ;//на ответ от клиента Hello, шлем ответ ОК
Но правильно ли это? =)
Спасибо. Случайно поставил видимо.
А как реализовать клиентскую часть, которая принимает данные не от серверной части, а от удаленного хоста, например какой-либо хаб DC?
Для такой реализации необходимо знать на каком порту висит сервер, какие команды принимает, какие отправляет и какой порт слушать.
Вы отправляете какую-то команду и смотрите, что он отвечает. В зависимости от ответа выполняете действия.
Серго
Для реализации клиентской части нужно знать какой протокол используется. Узнать это вы можете просмотрев сниффером пакеты.
Спасибо. Очень хороший материал.
Я недавно столкнулся с одной проблемой. В сети есть сервер, рассылающий датаграммы на широковещательный адрес сети на определенный порт. Есть клиентский комп, с определенным IP адресом в этой сети. Так вот, можно ли иметь на этом клиентском копме не одну, а множество программ, слушающих эти датаграммы на этом определенном порту? Сразу скажу, что у меня не получилось это реализовать. Если одна программа делает Socket.Bind на том порту и на том интерфейсе, где находится эта сеть, то при попытке другой программой сделать Bind на том-же интерфейсе на том-же порту, происходит исключение.
А на какое исключение падает? Я вообще думаю, что порт может слушать несколько программ. Только они не должны жестко перехватывать сообщение. Но точно сказать не могу. Так сильно в эту тему не углублялся.
Исключение: «System.Net.Sockets.SocketException: Обычно разрешается только одно использование адреса сокета (протокол/сетевой адрес/порт) в System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) в System.Net.Sockets.Socket.Bind(EndPoint localIP)…»
что то я не пойму, что нужно подключать что б это все заработало, может кто то прикрепит ссылку на готовый рабочий проект. буду благодарен
Мой using, который используется в проекте с этим кодом:
using System;
using System.Text;
using System.ServiceProcess;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections;
using System.Diagnostics;
очень полезная статья! но все таки не могу разобраться с отправкой данных GPS на свой сервер…=( если кто-нибудь сможет помочь напишите на chita-k@mail.ru
Зачем этот код? Зачем слушать порт под номером 0?
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
В клиенте массив data неправильно объявлен
А как правильно надо?
Смысл использовать один порт для нескольких программ?
Пусть каждая слушает свой порт, а сервер отправляет нужные данные на нужные порты, дело вто)