Сокеты в C ++ и C # под Windows не работают должным образом - PullRequest
0 голосов
/ 10 января 2012

Я использую сокеты UDP для связи приложения C ++ с приложением C #.

В направлении C # -> C ++ все, кажется, работает нормально, но наоборот, это то, что движет мнойорехи.

Связь работает, но сообщения запаздывают (например, с задержкой в ​​2 секунды) в приложении C #, даже если они отправляются каждый кадр (это 3D-приложение), а код приемавыполнение каждые 10 мс.

Мне нужно в реальном времени, так что это очень болезненная проблема.Как вы думаете, это может быть связано с потерями пакетов?Тогда почему они не теряются в другом направлении?

EDIT:

Код приложения C # для синхронизации данных:

    public void RecibeDatos()
    {
       if (MessageReceived && U != null && S != null)
       {
          MessageReceived = false;
          //Console.WriteLine("listening for messages");
          U.BeginReceive(ReceiveCallback, S);
       }
    }

    public void ReceiveCallback(IAsyncResult ar)
    {
        UdpClient u = ((UdpState)(ar.AsyncState)).U;
        IPEndPoint e = ((UdpState)(ar.AsyncState)).E;

        receivedBytes = u.EndReceive(ar, ref e);
        //int currentProtocol = (int) numero;
        //ResetSignal = reset > 0;

        //Console.WriteLine("Received: " + currentProtocol);
        MessageReceived = true;
    }

C ++ Код для отправки данных:

    float indiceFloat[1];
    indiceFloat[0] = indice_protocolo_actual;
    sender->setBuffer((void *)indiceFloat, sizeof(indiceFloat));
    sender->sync();

метод синхронизации в классе J_Enviar (отправитель):

    void J_Enviar::sync( void )
    {
        if(!_initialized) init();

        if( _buffer == 0L )
        {
            fprintf( stderr, "Broadcaster::sync() - No buffer\n" );
            return;
        }

    #if defined (WIN32) && !defined(__CYGWIN__)
        unsigned int size = sizeof( SOCKADDR_IN );
        sendto( _so, (const char *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
        int err = WSAGetLastError ();
        if (err!=0) 
            fprintf( stderr, "Broadcaster::sync() - error %d\n",err );
    #else
        unsigned int size = sizeof( struct sockaddr_in );
        sendto( _so, (const void *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
    #endif

    }

Предоставление полного кода SocketManager для получения конца C #:

using System;
using System.Net;
using System.Net.Sockets;

namespace WpfApplication1
{
public class SocketManager
{
    private static SocketManager _instance = null;
    static readonly object Padlock = new object();

    private IPEndPoint E;
    private UdpClient U;
    private UdpState S;

    private byte[] receivedBytes;


    public static bool MessageReceived = true;

    private SocketManager()
    {

    }

    public byte[] ReceivedBytes
    {
        get { return receivedBytes; }
    }

    public static SocketManager Instance
    {
        get
        {
            lock(Padlock)
            {
                return _instance ?? (_instance = new SocketManager());
            }
        }
    }

    public void CreateReceivingSocket(IPAddress a, int puerto)
    {

        if(E==null || (E.Address != a && E.Port != puerto))
        {
            E = new IPEndPoint(a, puerto);
            U = new UdpClient(puerto);
            S = new UdpState { E = E, U = U };
        }

    }


    public void ReceiveCallback(IAsyncResult ar)
    {
        UdpClient u = ((UdpState)(ar.AsyncState)).U;
        IPEndPoint e = ((UdpState)(ar.AsyncState)).E;

        receivedBytes = u.EndReceive(ar, ref e);
        //int currentProtocol = (int) numero;
        //ResetSignal = reset > 0;

        //Console.WriteLine("Received: " + currentProtocol);
        MessageReceived = true;
    }

    public void RecibeDatos()
    {
        if (MessageReceived && U != null && S != null)
        {
            MessageReceived = false;
            //Console.WriteLine("listening for messages");
            U.BeginReceive(ReceiveCallback, S);
        }
    }

    public void CloseConnection()
    {
        if (E != null)
        {
            E.Port = 5502;
            E = null;
        }
        if (U != null)
            U.Close();

    }
}

public class UdpState
{
    public IPEndPoint E;
    public UdpClient U;
}
}

И это мой диспетчерский щелчок, которыйзаставляет программу получать каждые 10 мс:

        _dispatcherTimer.Tick += DispatcherTimerTick;
        _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
        _dispatcherTimer.Start();

    private void DispatcherTimerTick(object sender, EventArgs e)
    {
        _exp1Class.sendData();
        _sm.RecibeDatos();
        byte[] recibidos = _sm.ReceivedBytes;
        if (recibidos != null)
        {
            float numero = BitConverter.ToSingle(recibidos, 0);

            _exp1Class.CurrentProtocol = (int) numero;
        }
    }

1 Ответ

1 голос
/ 10 января 2012

Не вижу, когда вы запускаете свой первый BeginReceive. (Ах, это сделано с вашего первого таймера, я думаю?) Это должно быть инициировано, как только вы будете готовы к получению данных. Во-вторых, ваш ReceiveCallback должен взять полученные данные и поместить их в какую-то очередь и немедленно снова вызвать BeginReceive. В противном случае вы задерживаете приход следующего кадра данных до тех пор, пока предыдущий не будет использован. Наконец, обратите внимание на проблемы с многопоточностью, поскольку каждый из них использует таймер многопоточности и обратный вызов UDP. Он работает в отдельных потоках от основного потока вашего приложения.

Единственная причина, по которой ваш код работает вообще, в том, что вы предварительно инициализировали MessageReceived = true даже до того, как получили какие-либо обратные вызовы. Когда происходит первый тик, вызов RecibeDatos вызывает BeginReceive, потому что для этого bool установлено значение true.

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

Вот грубые шаги:

Во-первых, при запуске (или включении, запуске и т. Д.) Вы должны вызвать BeginReceive. Теперь вы получите уведомление, когда данные поступят.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...