Windows下C++和C#中的套接字工作不正常
本文关键字:套接字 工作 不正常 C++ Windows | 更新日期: 2023-09-27 18:19:58
我使用UDP套接字与C#应用程序进行通信。
在C#->C++的方向上,一切似乎都很好,但另一方面却让我抓狂。
通信确实有效,但C#应用程序中的消息越来越晚(比如延迟2秒),尽管它们每帧都在发送(这是一个3D应用程序),并且接收代码每10毫秒执行一次。
我需要实时,所以这是一个非常痛苦的问题。你认为这可能与数据包丢失有关吗?那他们为什么不迷失在另一个方向呢?
编辑:
用于同步数据的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_Envivar(发送方)类上的同步方法:
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
}
为接收C#端提供完整的SocketManager代码:
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;
}
}
这是我的dispatchertimerclick,它使程序每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;
}
}
我不知道你是什么时候开始第一次BeginReceive的。(啊,我想这是从你的第一个计时器开始的吗?)应该在你准备好接收数据后立即启动。其次,ReceiveCallback应该获取接收到的数据,并将其放入某种队列中,然后立即再次调用BeginReceive。否则,您将等待下一个数据帧的到达,直到前一个数据被消耗掉。最后,注意线程问题,因为线程计时器和UDP回调将分别在应用程序主线程的独立线程上运行。
代码工作的唯一原因是,即使在收到任何回调之前,您也预先初始化了MessageReceived=true。当第一次勾选发生时,对RecibeDatos的调用调用BeginReceive,因为该bool设置为true。
把BeginReceive想象成"当你从网络上获得一些数据时给我回电话"。您不需要使用计时器轮询网络。(如果您的应用程序需要,您可以选择通过计时器使用这些数据,但暂且不谈)。
以下是粗略的步骤:
首先,在启动(或启用、运行等)时,您应该调用BeginReceive。现在,当数据到达时,您将收到一个通知。
其次,当回调发生时,您可以使用EndReceive完成对数据字节的读取。这些数据通常会被缓冲或以其他方式调度。然后,您应该再次调用BeginReceive(在回调中),以便在下一组数据可用时再次收到通知。它变成了一种异步循环。
问题是如何处理您正在读取的数据。您可以考虑将数据放入队列中,然后让计时器将这些数据帧从队列中弹出进行处理。请注意,数据可能会在您的Tick之间多次到达。