Подключение с помощью сокета датаграмм (HTML)

[ Эта статья адресована разработчикам приложений среды выполнения Windows для Windows 8.x и Windows Phone 8.x. При разработке приложений для Windows 10 см. раздел последняя документация]

В этом разделе: Использование UDP для отправки и получения сетевых данных в приложении среды выполнения Windows с помощью DatagramSocket.

В клиентском компоненте этого примера создается сокет UDP. Этот сокет используется для отправки и получения данных, а затем закрывается. В серверном компоненте этого примера создается сокет UDP для ожидания входящих сетевых пакетов. Этот сокет получает входящие пакеты UDP от клиента, отправляет данные клиенту и закрывает сокет. Этот пример предоставляется на JavaScript, C# и C++.

Клиентский компонент примера демонстрирует следующие функции:

  • Использование класса DatagramSocket для создания сокета UDP, позволяющего клиенту отправлять и получать данные.
  • Добавление обработчика для события DatagramSocket.MessageReceived, указывающего, что датаграмма UDP получена на объекте DatagramSocket.
  • Настройка удаленной конечной точки для сетевого сервера UDP, куда можно отправлять пакеты с помощью одного из методов DatagramSocket.ConnectAsync.
  • Отправка данных на сервер с помощью объекта Streams.DataWriter, который позволяет программисту записывать общие типы данных (например, целочисленные и строковые) в любой поток.
  • Закрытие сокета.

Серверный компонент примера демонстрирует следующие функции:

  • Использование класса DatagramSocket для создания сокета UDP, ожидающего и получающего входящие пакеты датаграмм, а также отправляющего пакеты.
  • Добавление обработчика для события DatagramSocket.MessageReceived, указывающего, что датаграмма UDP получена на объекте DatagramSocket.
  • Привязка сокета к имени локальной службы для ожидания входящих пакетов UDP с помощью метода DatagramSocket.BindServiceNameAsync.
  • Получение события DatagramSocket.MessageReceived, указывающего, что датаграмма UDP была получена на объекте DatagramSocket.
  • Получение данных от клиента с помощью обработчика DatagramSocket.MessageReceived. Объект DatagramSocketMessageReceivedEventArgs, передаваемый обработчику DatagramSocket.MessageReceived, позволяет приложению получать данные от клиента, а также определяет удаленный адрес и порт, отправившие данные.
  • Закрытие сокета.

Примечание  Использование данного примера требует доступа к сети с использованием интерфейса замыкания на себя.

 

Цель: Создание сетевого подключения к другому компьютеру или устройству, используя сокет DatagramSocket.

Необходимые условия

В следующих примерах используется JavaScript. Подробнее о создании первого приложения: Создание первого приложения Магазина Windows на JavaScript.

Чтобы подготовить приложение Магазина Windows для работы в сети, необходимо настроить такую возможность в файле проекта Package.appxmanifest. Определение всех сетевых возможностей см. в разделе Настройка возможностей сетевой изоляции.

Инструкции

1. Создание нового проекта

  1. Откройте Microsoft Visual Studio 2013 и выберите Создать проект в меню Файл.
  2. В списке шаблонов выберите JavaScript.
  3. В разделе выберите Store apps.
  4. В этом разделе выберите Universal Apps, Windows apps или Windows Phone apps (в зависимости от целевой платформы), а затем — Пустое приложение.
  5. Назовите приложение socketsSample и нажмите ОК.

2. Задание характеристик для обеспечения доступа к сети

Если приложению нужен доступ к сети, для него необходимо задать возможности сетевого подключения. Приложению, использующему DatagramSocket для подключения к сетевой службе, понадобится набор возможностей сетевого подключения.

Если приложению требуется подключаться в качестве клиента к удаленным службам в Интернете, для этого требуется возможность Интернет (клиент). Если приложению необходима возможность подключаться от имени клиента к удаленным службам в домашней или рабочей сети, то потребуется возможность Частные сети (клиент и сервер).

Если приложению требуется использовать DatagramSocket для ожидания входящих подключений от удаленных конечных точек в Интернете, понадобится возможность Интернет (клиент и сервер) . Если приложению необходима возможность использовать DatagramSocket для ожидания входящих подключений от удаленных конечных точек в домашней или рабочей сети, то потребуется возможность Частные сети (клиент и сервер).

Примечание  В Windows Phone имеется только один тип возможности сетевого подключения — Интернет (клиент и сервер). Он обеспечивает приложению все формы доступа к сети.

 

Если серверный компонент этого примера, ожидающий входящих подключений, работает на том же устройстве, что и клиентский компонент, то требуется замыкание на себя. Приложения, разработанные и выполняемые в Visual Studio 2013, будут автоматически регистрироваться как освобожденные от ограничений замыкания на себя. Дополнительная информация: Как включить замыкание на себя и устранить неполадки сетевой изоляции.

Подробнее о доступе к сети: Как настроить возможности сетевой изоляции.

Эти действия нужны, чтобы настроить возможности сетевого подключения для приложения перед развертыванием, если оно обращается к веб-службе в Интернете либо в частной или рабочей сети.

  1. Откройте файл package.appxmanifest в Microsoft Visual Studio.

  2. Перейдите на вкладку Возможности.

  3. Чтобы создать версию примера для Windows, выберите возможности (Интернет (клиент) и Частные сети (клиент и сервер).

    Чтобы создать версию примера для Windows Phone, выберите возможность Интернет (клиент и сервер).

  4. Сохраните и закройте файл манифеста.

3. Добавление пользовательского интерфейса на HTML

  1. Откройте папку html. Создайте файл startListener.html и поместите следующий HTML-код в разделы <head> и <body>.

    
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/socketsSample.js"></script>
        <script src="/js/startListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                DatagramSocket is used to create the 'server' side of a connection. It listens on a 'service name' (often a port number) and 
                each time a datagram is received on the port number it fires MessageReceived event.
            </p>
            <p>
                <label for="serviceNameAccept">Service Name:</label>
                <input id="serviceNameAccept" type="text" />
            </p>
            <p>
                <button id="buttonStartListener">Listen</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  2. Откройте папку html. Создайте файл connectToListener.html и поместите следующий HTML-код в разделы <head> и <body>.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/connectToListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Next, you need the 'other side of the connection' -- you need to connect to a listener. The host name
                and service name (often a port number) to connect to are the 'Host name:' and 'Service name:' entries.
                The service name should match what you started to listen to!
            </p>
            <p>
                The connection will automatically use IPv6 as needed. It will also resolve internationalized
                domain names.
            </p>
            <p>
                Due to the network security system, you cannot connect to other applications running on the same
                machine. This means that you can only use 'localhost' to connect to the same application (specifically,
                you can connect to a listener on the same machine running in the same app container)
            </p>
            <p>
                <label for="hostNameConnect">Host Name:</label>
                <input id="hostNameConnect" type="text" disabled="disabled" />
            </p>
            <p>
                <label for="serviceNameConnect">Service Name:</label>
                <input id="serviceNameConnect" type="text" />
            </p>
            <p>
                <button id="buttonOpen">Connect Now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  3. Откройте папку html. Создайте файл sendData.html и поместите следующий HTML-код в разделы <head> и <body>.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/sendData.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Now you can send data to the "server". Sending data is often done with the DataWriter
                object; it will write to the socket stream.
            </p>
            <p>
                <button id="buttonSend">Send 'hello' now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  4. Откройте папку html. Создайте файл closeSocket.html и поместите следующий HTML-код в разделы <head> и <body>.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/closeSocket.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Lastly, you can close all sockets.
            </p>
            <p>
                If you don't close your socket, it will be closed for you when the application exits.
            </p>
            <p>
                <button id="buttonClose">Close all sockets</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    

4. Определение примера и сценариев

На этом этапе в коде определяется пример, а также HTML-файлы и сценарии, которые в нем используются. Кроме того, в коде добавляются прослушиватели событий и происходит запуск приложения. На выбор пользователю предлагается несколько сценариев: запустить прослушиватель сокета, запустить клиент для подключения к прослушивателю, организовать отправку данных клиентом на сервер и закрыть сокеты.

  1. Откройте папку js. Откройте файл default.js и добавьте в него следующий код.

        var sampleTitle = "DatagramSocket";
    
        var scenarios = [
            { url: "/html/startListener.html", title: "Start DatagramSocket Listener" },
            { url: "/html/connectToListener.html", title: "Connect to Listener" },
            { url: "/html/sendData.html", title: "Send Data" },
            { url: "/html/closeSocket.html", title: "Close Socket" }
        ];
    
        function activated(eventObject) {
            if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
                // Use setPromise to indicate to the system that the splash screen must not be torn down
                // until after processAll and navigate complete asynchronously.
                eventObject.setPromise(WinJS.UI.processAll().then(function () {
                    // Navigate to either the first scenario or to the last running scenario
                    // before suspension or termination.
                    var url = WinJS.Application.sessionState.lastUrl || scenarios[0].url;
                    return WinJS.Navigation.navigate(url);
                }));
            }
        }
    
        WinJS.Navigation.addEventListener("navigated", function (eventObject) {
            var url = eventObject.detail.location;
            var host = document.getElementById("contentHost");
            // Call unload method on current scenario, if there is one
            host.winControl && host.winControl.unload && host.winControl.unload();
            WinJS.Utilities.empty(host);
            eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {
                WinJS.Application.sessionState.lastUrl = url;
            }));
        });
    
        WinJS.Namespace.define("SdkSample", {
            sampleTitle: sampleTitle,
            scenarios: scenarios
        });
    
        WinJS.Application.addEventListener("activated", activated, false);
        WinJS.Application.start();
    

5. Определение переменных для сокетов и функций событий

На этом этапе в коде создается ряд переменных, в том числе сокет прослушивателя, сокет клиента и различные переменные для обработки ошибок и событий. Создаются переменные для отслеживания состояния клиентского сокета (подключен или закрывается). На этом этапе также определяются имя узла и имя службы (порт UDP) для подключения и отправки данных, а также имя локальной службы (порт UDP), в которой принимаются и получаются данные. В качестве имен удаленного узла, удаленной службы и локальной службы устанавливаются значения по умолчанию, которые можно изменить в пользовательском интерфейсе.

  • Откройте папку js. Откройте файл socketsSample.js и добавьте в него следующий код.

    var socketsSample = {};
    
    (function () {
        "use strict";
    
        socketsSample.listener = null;
        socketsSample.listenerOutputStream = null;
        socketsSample.listenerPeerAddress = null;
        socketsSample.listenerPeerPort = null;
        socketsSample.clientSocket = null;
        socketsSample.clientDataWriter = null;
        socketsSample.connected = false;
        socketsSample.closing = false;
        socketsSample.bindingToService = false;
    
        socketsSample.serviceNameAccept = "22112";
        socketsSample.hostNameConnect = "localhost";
        socketsSample.serviceNameConnect = "22112";
    
        socketsSample.close = function () {
    
            socketsSample.closing = true;
    
            if (socketsSample.listener) {
                socketsSample.listener.close();
            }
    
            if (socketsSample.clientSocket) {
                socketsSample.clientSocket.close();
            }
    
            socketsSample.listener = null;
            socketsSample.listenerOutputStream = null;
            socketsSample.listenerPeerAddress = null;
            socketsSample.listenerPeerPort = null;
            socketsSample.clientSocket = null;
            socketsSample.clientDataWriter = null;
            socketsSample.connected = false;
        };
    
        socketsSample.displayStatus = function (message) {
            document.getElementById("statusBox").innerHTML = message;
        };
    
        socketsSample.displayOutput = function (message) {
            document.getElementById("outputBox").innerHTML = message;
        };
    
        socketsSample.setValues = function () {
            var serviceNameAcceptInput = document.getElementById("serviceNameAccept");
            var hostNameConnectInput = document.getElementById("hostNameConnect");
            var serviceNameConnectInput = document.getElementById("serviceNameConnect");
    
            if (serviceNameAcceptInput) {
                serviceNameAcceptInput.value = socketsSample.serviceNameAccept;
            }
            if (hostNameConnectInput) {
                hostNameConnectInput.value = socketsSample.hostNameConnect;
            }
            if (serviceNameConnectInput) {
                serviceNameConnectInput.value = socketsSample.serviceNameConnect;
            }
        };
    
        socketsSample.getValues = function (evt) {
            switch (evt.target.id) {
                case "serviceNameAccept":
                    socketsSample.serviceNameAccept = evt.target.value;
                    break;
                case "hostNameConnect":
                    socketsSample.hostNameConnect = evt.target.value;
                    break;
                case "serviceNameConnect":
                    socketsSample.serviceNameConnect = evt.target.value;
                    break;
            }
        };
    })();
    

6. Создание прослушивателя и запуск прослушивания службы с заданным именем (номером порта)

В коде этого раздела создается прослушиватель и запускается прослушивание. Кроме того, здесь добавляются функции для обработки событий, когда пользователь дает команду прослушивателю выполнить привязку к IP-адресу и UDP-порту, принять подключение и прочесть данные, переданные из клиента.

Примечание  Хотя данный конкретный пример самодостаточен (клиент и сервер располагаются в одном приложении), обычно клиентское и серверное приложения существуют отдельно.

 

  • Откройте папку js. Откройте файл startListener.js и добавьте в него следующий код.

        var page = WinJS.UI.Pages.define("/html/startListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonStartListener").addEventListener("click", startListener, false);
                document.getElementById("serviceNameAccept").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function startListener() {
            var serviceName = document.getElementById("serviceNameAccept").value;
            if (serviceName === "") {
                socketsSample.displayStatus("Please provide a service name.");
                return;
            }
    
            if (socketsSample.listener) {
                socketsSample.displayStatus("Already have a listener; call close to close the listener.");
                return;
            }
    
            socketsSample.closing = false;
            socketsSample.bindingToService = true;
            socketsSample.listener = new Windows.Networking.Sockets.DatagramSocket();
            socketsSample.listener.addEventListener("messagereceived", onServerMessageReceived);
            socketsSample.displayStatus("Server: listener creation started.");
            socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
                socketsSample.displayStatus("Server: listener creation completed.");
                socketsSample.bindingToService = false;
            }, onError);
        }
    
        function onServerMessageReceived(eventArgument) {
            if (socketsSample.listenerOutputStream) {
                echoMessage(socketsSample.listenerOutputStream, eventArgument);
                return;
            }
    
            socketsSample.listener.getOutputStreamAsync(eventArgument.remoteAddress, eventArgument.remotePort).done(function (outputStream) {
                if (!socketsSample.listenerOutputStream) {
                    socketsSample.listenerOutputStream = outputStream;
                    socketsSample.listenerPeerAddress = eventArgument.remoteAddress;
                    socketsSample.listenerPeerPort = eventArgument.remotePort;
                }
    
                echoMessage(socketsSample.listenerOutputStream, eventArgument);
            });
        }
    
        function echoMessage(outputStream, eventArgument) {
            if (socketsSample.listenerPeerAddress !== eventArgument.remoteAddress ||
                socketsSample.listenerPeerPort !== eventArgument.remotePort) {
                socketsSample.displayStatus("Got datagram from " + eventArguments.remoteAddress + ":" + eventArguments.remotePort +
                    ", but already 'connected' to " + socketsSample.listenerPeerAddress + ":" + socketsSample.listenerPeerPort);
                return;
            }
    
            outputStream.writeAsync(eventArgument.getDataReader().detachBuffer()).done(function () {
                // Do nothing - client will print out a message when data is received.
            });
        }
    
        function onError(reason) {
            // Clean up a listener if we failed to bind to a port.
            if (socketsSample.bindingToService) {
                socketsSample.listener = null;
                socketsSample.bindingToService = false;
            }
    
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

7. Создание сокета и подключение к удаленной конечной точке

На этом этапе в код добавляется функция, создающая сокет для подключения к удаленной конечной точке (обычно это сервер) с помощью метода DatagramSocket.ConnectAsync. Добавляется функция для обработки ситуаций, когда клиент получает сообщение. Кроме того, добавляется функция, обрабатывающая ситуации, когда при попытке клиента установить подключение происходит ошибка.

  • Откройте папку js. Откройте файл connectToListener.js и добавьте в него следующий код.

        var page = WinJS.UI.Pages.define("/html/connectToListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonOpen").addEventListener("click", openClient, false);
                document.getElementById("hostNameConnect").addEventListener("change", socketsSample.getValues, false);
                document.getElementById("serviceNameConnect").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function openClient() {
            var serviceName = document.getElementById("serviceNameConnect").value;
            if (serviceName === "") {
                socketsSample.displayStatus("Please provide a service name.");
                return;
            }
    
            // By default 'hostNameConnect' is disabled and host name validation is not required. When enabling the text
            // box validating the host name is required since it was received from an untrusted source (user input).
            // Note that when enabling the text box users may provide names for hosts on the intErnet that require the
            // "Internet (Client)" capability.
            var hostName;
            try {
                hostName = new Windows.Networking.HostName(document.getElementById("hostNameConnect").value);
            } catch (error) {
                socketsSample.displayStatus("Error: Invalid host name.");
                return;
            }
    
            if (socketsSample.clientSocket) {
                socketsSample.displayStatus("Already have a client; call close to close the listener and the client.");
                return;
            }
    
            socketsSample.closing = false;
            socketsSample.clientSocket = new Windows.Networking.Sockets.DatagramSocket();
            socketsSample.clientSocket.addEventListener("messagereceived", onMessageReceived);
            socketsSample.displayStatus("Client: connection started.");
            socketsSample.clientSocket.connectAsync(hostName, serviceName).done(function () {
                socketsSample.displayStatus("Client: connection completed.");
                socketsSample.connected = true;
            }, onError);
        }
    
        function onMessageReceived(eventArgument) {
            try {
                var messageLength = eventArgument.getDataReader().unconsumedBufferLength;
                var message = eventArgument.getDataReader().readString(messageLength);
                socketsSample.displayStatus("Client: receive message from server \"" + message + "\"");
            } catch (exception) {
                status = Windows.Networking.Sockets.SocketError.getStatus(exception.number);
                if (status === Windows.Networking.Sockets.SocketErrorStatus.connectionResetByPeer) {
                    socketsSample.displayStatus("Peer does not listen on the specific port. Please make sure that you run step 1 first " +
                    "or you have a server properly working on a remote server.");
                } else {
                    socketsSample.displayStatus("Error happened when receiving a datagram: " + exception.message);
                }
            }
        }
    
        function onError(reason) {
            socketsSample.clientSocket = null;
    
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

8. Передача и чтение данных в клиенте

На этом этапе в код добавляется функция для передачи данных на удаленную конечную точку UDP с использованием методов класса Windows.Storage.Streams.DataWriter.

  • Откройте папку js. Откройте файл sendData.js и добавьте в него следующий код.

        var page = WinJS.UI.Pages.define("/html/sendData.html", {
            ready: function (element, options) {
                document.getElementById("buttonSend").addEventListener("click", sendHello, false);
            }
        });
    
        function sendHello() {
            if (!socketsSample.connected) {
                socketsSample.displayStatus("Client: you must connect the client before using it.");
                return;
            }
    
            if (!socketsSample.clientDataWriter) {
                socketsSample.clientDataWriter = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
            }
    
            var string = "Hello World";
            socketsSample.clientDataWriter.writeString(string);
    
            socketsSample.displayStatus("Client sending: " + string + ".");
            socketsSample.clientDataWriter.storeAsync().done(function () {
                socketsSample.displayStatus("Client sent: " + string + ".");
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

9. Закрытие сокетов

На этом этапе в коде закрываются сокеты с использованием метода DatagramSocket.Close. Когда сокеты закрываются, все невыполненные операции завершаются и вызываются подпрограммы обработки ошибок.

  • Откройте папку js. Откройте файл socketClose.js и добавьте в него следующий код.

        var page = WinJS.UI.Pages.define("/html/sendData.html", {
            ready: function (element, options) {
                document.getElementById("buttonSend").addEventListener("click", sendHello, false);
            }
        });
    
        function sendHello() {
            if (!socketsSample.connected) {
                socketsSample.displayStatus("Client: you must connect the client before using it.");
                return;
            }
    
            if (!socketsSample.clientDataWriter) {
                socketsSample.clientDataWriter = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
            }
    
            var string = "Hello World";
            socketsSample.clientDataWriter.writeString(string);
    
            socketsSample.displayStatus("Client sending: " + string + ".");
            socketsSample.clientDataWriter.storeAsync().done(function () {
                socketsSample.displayStatus("Client sent: " + string + ".");
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

10. Запуск приложения

  • Чтобы запустить приложение, нажмите клавишу F5 в Visual Studio, и проект будет запущен. Выберите кнопки для запуска прослушивателя, подключения к нему клиента, передачи данных и закрытия сокетов.

Краткая сводка и дальнейшие действия

В этом разделе вы создали приложение, использующее сокет датаграмм UDP, чтобы установить сетевое подключение и отправлять данные с помощью объекта DatagramSocket. В приложении было также показано, как прослушивать порт UDP и получать данные.

Исходный код и файлы сборки для этого раздела находятся в примере DatagramSocket.

Вы также можете создать сетевое подключение для передачи и приема данных, используя сокет потока. Пример см. в разделе Как подключаться с помощью сокета потока.

Связанные разделы

Другие ресурсы

Подключение через сокеты

Настройка возможностей сетевой изоляции

Как подключаться с помощью сокета потока

Как задать время ожидания при выполнении операций с сокетами

Использование расширенных элементов управления сокета

Устранение неполадок и отладка сетевых подключений

Ссылки

DatagramSocket

Windows.Networking

Windows.Networking.Sockets

Примеры

Пример DatagramSocket