Передача файла через UDP с помощью Socket
Попытаюсь ввести в курс дела
Я пишу 2 программы, 1й это консольный сервер, 2й это приложение WPF. Приложение отправляет запрос сделать скриншот серверу и сервер получив сообщение делает скриншот и отправляет его по байтам в приложение чтобы отобразить этот скриншот но у меня где-то ошибка которую я не могу найти.
Вот код сервера
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var listenerEP = new IPEndPoint(IPAddress.Loopback, 27001);
EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
listener.Bind(listenerEP);
var buffer = new byte[ushort.MaxValue];
Bitmap bit = new Bitmap(1920, 1080);
Size size = new Size(bit.Width, bit.Height);
Graphics graphic = Graphics.FromImage(bit);
ImageConverter converter = new ImageConverter();
while (true)
{
var len = listener.ReceiveFrom(buffer, ref endPoint);
// приём сообщения сделать скрин
if (Encoding.Default.GetString(buffer, 0, len) == "screen")
{
Console.Clear();
Console.Clear();
// получаем скриншот
graphic.CopyFromScreen(0, 0, 0, 0, size);
// преобразовываем сскриншот в биты
byte[] imgByte = (byte[])converter.ConvertTo(bit, typeof(byte[]));
Console.WriteLine($"imgByte Length {imgByte.Length}");
// создаём массив для отправки битов по частям
ArraySegment<byte> segment = null;
int indStart = 0;
int elem = 1000;
int count = 1;
int byteCount = imgByte.Length;
// получаем количество пакетов для отправки
while (true)
{
byteCount -= 1000;
count++;
if (byteCount < 0)
break;
}
// отправляем количество пакетов которые будут отправленны
byte[] bytesCount = BitConverter.GetBytes(count);
listener.SendTo(bytesCount, SocketFlags.None, endPoint);
Console.WriteLine($"countBytes {bytesCount.Length}");
// отправка массива битов скриншота
do
{
segment = new ArraySegment<byte>(imgByte, indStart, elem);
listener.SendTo(segment, SocketFlags.None, endPoint);
indStart = elem;
elem += 1000;
if (imgByte.Length % 1000 < 1000)
{
elem = imgByte.Length % 1000;
}
Console.WriteLine($"count {count--}");
} while (count != 0);
Console.WriteLine($"Send! {imgByte.Length}");
}
}
А это код приложения
var client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
EndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 27001);
// отправка команды сделать скрин
client.SendTo(Encoding.UTF8.GetBytes("screen"), endPoint);
// приём сообщения о количестве отправленных пакетов
byte[] countBytes = new byte[sizeof(int)];
client.ReceiveFrom(countBytes, ref endPoint);
int count = BitConverter.ToInt32(countBytes, 0);
byte[] imageBytes = new byte[count * 1000];
byte[] img = new byte[count * 1000];
ArraySegment<byte> segment = null;
// приём массива байтов скриншота
for (int i = 0; i < count; i++)
{
segment = new ArraySegment<byte>(imageBytes, i*1000, 1000);
client.ReceiveFrom(segment, ref endPoint);
}
// собираем скриншот из байтов обратно
BitmapImage bitmapImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream(imageBytes))
{
stream.Seek(0, SeekOrigin.Begin);
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
Screen_shot.Source = bitmapImage;
}
client.Close();
данные между приложениями отправляются но полученный скриншот в итоге получается таким

Да, я знаю что такое UDP и лучше бы использовать TCP, но домашнее задание таково, что это надо сделать используя Socket(UDP), именно используя сокет с UDP (Вот само задание: Программа должна состоять из двух частей: клиента и сервера. Для написания программы следует использовать протокол UDP. Сервер должен уметь отправлять скриншоты клиенту. Клиент (в котором также будет встроен сервер для приема скриншотов) должен быть приложением WPF и получать скриншоты от сервера и отображать их на экране. )
Можете ли подсказать где тут ошибка? Может я как-то не правильно отправляю скрин или не правильно принимаю. Я уже несколько часов пытаюсь найти проблему и не получается.
Ответы (1 шт):
Намудрили вы что-то с подсчетом количества пакетов. Кто вам сказал, что будет отправлено за раз ровно столько байт сколько вы послали? Кто сказал что за раз будет получено ровно столько байт, сколько вы ожидаете? Нет таких гарантий, поэтому надо всегда смотреть в число, которое возвращает Socket.SendTo или Socket.ReceiveFrom. Оперируйте байтами, какая разница, сколько пакетов потребуется. Количество отправленных и принятых пакетов вообще может оказаться разным на отправляющей стороне и принимающей, из-за фрагментации при передаче. Это нормально. Главное - чтобы все отправленные байты были получены, и это следует контролировать.
Ну поехали, покажу пример на базе простого консольного .NET 6 приложения, где сервер и клиент в одном приложении.
Начну с простого - взятие скриншота без участия Winforms, ранее такой код я писал здесь.
[DllImport("user32.dll")]
static extern bool SetProcessDPIAware();
static Size GetMonitorSize()
{
IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle;
using Graphics g = Graphics.FromHwnd(hwnd);
return new Size((int)g.VisibleClipBounds.Width, (int)g.VisibleClipBounds.Height);
}
static Bitmap TakeScreenshot(Size size)
{
Bitmap bmp = new Bitmap(size.Width, size.Height);
using Graphics graphics = Graphics.FromImage(bmp);
graphics.CopyFromScreen(Point.Empty, Point.Empty, bmp.Size);
return bmp;
}
Далее установлю общую константу для размера пакета
const int packetSize = 8192;
Сервер
static void Server()
{
using Socket listener = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
EndPoint listenerEP = new IPEndPoint(IPAddress.Loopback, 27001);
EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
listener.Bind(listenerEP);
Span<byte> buffer = new byte[packetSize];
Size size = GetMonitorSize();
while (true)
{
int len = listener.ReceiveFrom(buffer, ref endPoint);
if (Encoding.UTF8.GetString(buffer[..len]) == "screen")
{
using Bitmap bmp = TakeScreenshot(size);
MemoryStream ms = new();
bmp.Save(ms, ImageFormat.Png);
Span<byte> bytes = ms.GetBuffer().AsSpan()[..(int)ms.Length];
Console.WriteLine($"[Server] Screenshot taken: {bytes.Length} bytes");
// для отладки
File.WriteAllBytes("sent.png", ms.ToArray());
BinaryPrimitives.WriteInt32LittleEndian(buffer, bytes.Length);
listener.SendTo(buffer[..4], SocketFlags.None, endPoint);
int position = 0;
while (position < bytes.Length - 1)
{
int next = Math.Min(position + packetSize, bytes.Length);
int bytesSent = listener.SendTo(bytes[position..next], SocketFlags.None, endPoint);
position += bytesSent;
Console.WriteLine($"[Server] Sent {bytesSent} Position: {position}");
}
Console.WriteLine("[Server] Image sent!");
break;
}
}
}
Клиент
static void Client()
{
Socket client = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
EndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 27001);
client.SendTo(Encoding.UTF8.GetBytes("screen"), endPoint);
Span<byte> buffer = new byte[packetSize];
client.ReceiveFrom(buffer, ref endPoint);
int length = BinaryPrimitives.ReadInt32LittleEndian(buffer[..4]);
Console.WriteLine($"[Client] Received length: {length} bytes");
MemoryStream ms = new();
int position = 0;
while (position < length - 1)
{
int bytesReceived = client.ReceiveFrom(buffer, ref endPoint);
ms.Write(buffer[..bytesReceived]);
position += bytesReceived;
Console.WriteLine($"[Client] Received: {bytesReceived} bytes");
}
Console.WriteLine($"[Client] Image received!");
// запись полученной картинки
File.WriteAllBytes("received.png", ms.ToArray());
client.Close();
}
И запускаю всё вместе
static async Task Main(string[] args)
{
SetProcessDPIAware();
Task srv = Task.Run(Server);
await Task.Delay(1000);
Task clt = Task.Run(Client);
await Task.WhenAll(srv, clt);
// проверка результата
if (File.ReadAllBytes("sent.png").SequenceEqual(File.ReadAllBytes("received.png")))
Console.WriteLine("[Main] Images are EQUAL!");
else
Console.WriteLine("[Main] Images are DIFFERENT!");
Console.WriteLine("Done.");
}
Вывод в консоль, отправка скриншота PNG размером 3840x2160
[Server] Screenshot taken: 660360 bytes
[Server] Sent 8192 Position: 8192
[Server] Sent 8192 Position: 16384
[Server] Sent 8192 Position: 24576
[Client] Received length: 660360 bytes
[Server] Sent 8192 Position: 32768
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 40960
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 49152
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 57344
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 65536
[Server] Sent 8192 Position: 73728
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 81920
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 90112
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 98304
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 106496
[Server] Sent 8192 Position: 114688
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 122880
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 131072
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 139264
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 147456
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 155648
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 163840
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 172032
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 180224
[Server] Sent 8192 Position: 188416
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 196608
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 204800
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 212992
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 221184
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 229376
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 237568
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 245760
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 253952
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 262144
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 270336
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 278528
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 286720
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 294912
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 303104
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 311296
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 319488
[Server] Sent 8192 Position: 327680
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 335872
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 344064
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 352256
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 360448
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 368640
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 376832
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 385024
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 393216
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 401408
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 409600
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 417792
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 425984
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 434176
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 442368
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 450560
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 458752
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 466944
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 475136
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 483328
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 491520
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 499712
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 507904
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 516096
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 524288
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 532480
[Server] Sent 8192 Position: 540672
[Server] Sent 8192 Position: 548864
[Server] Sent 8192 Position: 557056
[Server] Sent 8192 Position: 565248
[Server] Sent 8192 Position: 573440
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 581632
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 589824
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 598016
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 606208
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 614400
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 622592
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 630784
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 638976
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 647168
[Client] Received: 8192 bytes
[Server] Sent 8192 Position: 655360
[Client] Received: 8192 bytes
[Server] Sent 5000 Position: 660360
[Server] Image sent!
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Client] Received: 8192 bytes
[Client] Received: 5000 bytes
[Client] Image received!
[Main] Images are EQUAL!
Done.
В большинстве случаев этот код работает, а точнее, чем меньше файл, тем больше вероятность что он будет передан успешно. Но при потерях пакетов клиент зависнет, так как не умеет перезапрашивать потерянные данные и ожидает просто заявленное сервером количество байт. Если нужны какие-то гарантии, что данные доедут полностью, придется добавить отказоустойчивости в протокол приема-передачи уровня приложения, то есть тот протокол, который вы сами придумаете.