Запутался в сокетах с протоколом UDP в C # - PullRequest
6 голосов
/ 24 марта 2011

Я только начал изучать Sockets с помощью различных поисков в Google, но у меня возникли проблемы с выяснением того, как правильно использовать Sockets в C #, и мне нужна помощь.

У меня естьтестовое приложение (Windows Forms) и другого класса (который на самом деле находится в своем собственном .dll, но это не имеет значения) У меня есть весь код сервера / клиента для моего кода сокетов.

Вопрос 1)

В моем тестовом приложении на серверной части пользователь может нажать кнопку «начать прослушивание», и серверная часть моего приложения сокетов должна начать прослушивать соединения по указанному адресу и порту,пока все хорошо.

Однако приложение будет заблокировано, и я ничего не могу сделать, пока кто-то не подключится к серверу.Что делать, если никто не подключается?Как мне справиться с этим?Я мог бы указать время ожидания получения, но что тогда?Это исключение, что я могу с этим сделать?Мне бы хотелось, чтобы в главном приложении была какая-то активность, чтобы пользователь знал, что приложение не зависло, и ожидает подключения.Но если соединение не приходит, оно должно прервать тайм-аут и закрыть все.

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

Вопрос 2)

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

В заключение двух моих вопросов .... Я хотел бы приложение, которое не блокирует, так чтопользователь знает, что сервер ожидает соединения (например, с небольшой анимацией выделения).Если через какое-то время соединение никогда не устанавливается, я хочу закрыть все, что должно быть закрыто.Когда соединение установлено или если оно не происходит по прошествии некоторого времени, я хотел бы сообщить основному приложению о результате.

Вот часть моего кода, остальное похоже.Класс Packet - это пользовательский класс, представляющий мой пользовательский блок данных, на данный момент это просто набор свойств, основанных на enums, с методами для преобразования их в байты и обратно в свойства.

Функциякоторый начинает прослушивать подключения, выглядит примерно так:

public void StartListening(string address, int port) {
    try {
        byte[] bufferBytes = new byte[32];

        if(address.Equals("0.0.0.0")) {
            udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        } else {
            udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
        }

        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

        int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

        if(numBytesReceived == 0) {
            udpSocket.Close();
            return;
        }

        Packet syncPacket = new Packet(bufferBytes);

        if(syncPacket.PacketType != PacketType.Control) {
            udpSocket.Close();
            return;
        }
    } catch {
        if(udpSocket != null) {
            udpSocket.Close();
        }
    }
}

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

РЕДАКТИРОВАТЬ:

Я, вероятно, должен был заявить, что мои требования заключаются в использовании UDP и реализации этихвещи сам на уровне приложений.Вы можете рассматривать это как домашнее задание, но я не пометил как таковое, потому что код не имеет значения и не будет частью моей оценки, и моя проблема (мой вопрос) заключается в том, «как кодировать», так как мой опыт работы с сокетами минимален и неучил.

Однако я должен сказать, что я решил свою проблему на данный момент, я думаю ... Я использовал многопоточность в демонстрационном приложении, которое доставляло мне некоторые проблемы, теперь я использую его в соединениях протокола,имеет больше смысла, и я могу легко изменить свои свойства класса пользовательского протокола и прочитать их из демонстрационного приложения.

Я указал тайм-аут и выбрасываю SocketException, если он достигает тайм-аута.Всякий раз, когда возникает подобное исключение, сокетное соединение закрывается.Я просто говорю о связи рукопожатия, не более того.Если не обнаружено никаких исключений, код, вероятно, прошел гладко, и соединение установлено.

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

Ответы [ 2 ]

9 голосов
/ 24 марта 2011

Вы ошиблись.

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

TCP, с другой стороны, основан на соединении. Вы подключаетесь, отправляете / получаете и, наконец, отключаетесь. TCP основан на потоке (в то время как UDP основан на сообщениях), что означает, что вы можете получить половину сообщения при первом чтении и другую половину при втором чтении. TCP обещает вам, что все придет и в правильном порядке (или умрет, пытаясь;). Поэтому использование TCP означает, что у вас должна быть какая-то логика, чтобы знать, когда пришло полное сообщение, и буфер, который вы используете для создания полного сообщения.

Следующий большой вопрос был о блокировке. Поскольку вы новичок в этом, я рекомендую вам использовать Threads для обработки сокетов. Поместите сокет слушателя в один поток, а каждый соединительный сокет в отдельный поток (5 подключенных клиентов = 5 потоков).

Я также рекомендую использовать TCP, поскольку проще создавать полные сообщения, чем упорядочивать сообщения и создавать систему транзакций (что понадобится, если вы хотите, чтобы все сообщения поступали к клиентам или от них).

Обновление

У вас все еще неверный UDP. Close не делает ничего, кроме очистки системных ресурсов. Вместо этого вы должны сделать что-то вроде этого:

public void MySimpleServer(string address, int port) 
{
    try 
    {
        byte[] bufferBytes = new byte[32];

        if(address.Equals("0.0.0.0")) {
            udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        } else {
            udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
        }

        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        while (serverCanRun)
        {
            int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

            // just means that one of the clients closed the connection using Shutdown.
            // doesnt mean that we cant continue to receive.
            if(numBytesReceived == 0)
                continue; 

            // same here, loop to receive from another client.
            Packet syncPacket = new Packet(bufferBytes);
            if (syncPacket.PacketType != PacketType.Control)
                continue; 

            HandlePacket(packet, endPoint);
        }
    } catch {
        if(udpSocket != null) {
            udpSocket.Close();
        }
    }
}

См? так как нет соединения, это просто пустая трата времени, чтобы закрыть UDP-сокет, чтобы начать слушать с другого. Один и тот же сокет может получать от ВСЕХ клиентов udp, которые знают правильный порт и адрес. Вот для чего remoteEndPoint. Он сообщает клиенту, который отправил сообщение.

Обновление 2

Небольшое обновление для краткого изложения всех моих комментариев.

UDP без установления соединения. Вы никогда не сможете определить, было ли соединение установлено или отключено. Метод Close на UDP-сокете освобождает только системные ресурсы. Вызов на client.Close() не уведомит сокет сервера (как это будет с TCP).

Лучший способ проверить, открыто ли соединение, - это создать пакет типа ping / pong. клиент отправляет сообщение PING, а сервер отвечает PONG. Помните, что UDP не будет пытаться повторно отправить ваши сообщения, если они не приходят. Поэтому вам необходимо повторно отправить PING несколько раз, прежде чем предположить, что сервер не работает (если вы не получаете PONG).

Что касается закрытия клиентов, вам нужно отправить собственное сообщение на сервер, сообщающее, что клиент прекратит общаться с сервером. Для надежности то же самое и здесь, продолжайте повторную отправку сообщения BYE, пока не получите ответ.

imho, если вам нужна надежность, вы должны внедрить транзакционную систему для UDP. SIP (google rfc3261) является примером протокола, который использует транзакции по UDP.

0 голосов
/ 24 марта 2011

Из вашего описания я чувствую, что вы должны использовать TCP-сокеты вместо UDP.Разница в

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

UDP - вы ожидаете сообщения на каком-то IP: порту.Пользователь, который хочет общаться, просто отправляет сообщение через UDP.Вы получите сообщение через UDP.Порядок доставки не гарантируется.Это больше похоже на отправку кому-нибудь обычной почты.Не выделен выделенный канал связи.

Теперь перейдем к вашей проблеме

Сервер

  1. Создайте семейство сокетов с TCP.
  2. Либо создайте поток и примите соединение в этом потоке, либо используйте API-интерфейс BeginAccept Socket.
  3. В главном потоке вы по-прежнему можете отображать тикер или все, что хотите.

Клиент

  1. Подключение к серверу.
  2. Связь путем отправки и получения данных.
...