我在C#中编写了一个小型UDP客户端服务器类,用于在Linux和Windows机器之间提供通信。
在Windows中用C#实现UDP客户端和服务器是我最初为Linux编写的C ++代码的直接重写。
我在Linux机器之间的运行期间没有问题,但Linux和Windows链接之间偶尔会出现间歇性问题。
由于应用程序的原因,我需要对UDP套接字进行快速,非阻塞的操作。
由于一个客户端是Linux,C#下的代码我不得不使用一些魔术编组。
以下是代码:
public bool Connect(string sIPAddr, int portNumber)
{
try
{
if (portNumber > 65535 && portNumber < 0)
{
this._isReady = false;
return this._isReady;
}
this._ipPort = portNumber;
this._ipAddress = IPAddress.Parse(sIPAddr);
IPEndPoint ipep = new IPEndPoint(this._ipAddress, this._ipPort);
this._myUDPClient = new Socket(ipep.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
this._myUDPClient.Blocking = false;
this._myUDPClient.Connect(this._ipAddress, this._ipPort);
this._isReady = true;
return this._isReady;
}
catch (Exception)
{
this._isReady = false;
return this._isReady;
}
}
我在UDP上使用connect来简化发送和接收呼叫。
当我尝试从套接字读取时,问题就出现了。
更多代码:
public bool NewMessageReceived()
{
try
{
if (this._newMessaageReceived)
{
return this._newMessaageReceived;
}
else
{
_messageBuffer = new byte[65507];
int numBytesRcvd = _myUDPClient.Receive(this._messageBuffer, 65507, SocketFlags.None);
Marshal.Copy(_messageBuffer, 0, _pmessageBuffer, 65507);
if (numBytesRcvd < 0)
{
this._newMessaageReceived = false;
// TODO: Add Socket Error Checking
}
else
{
this._newMessaageReceived = true;
}
Array.Clear(_messageBuffer, 0, _messageBuffer.GetLength(0));
return this._newMessaageReceived;
}
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show(e.Message);
return false;
}
}
我在两台机器上都运行了Wireshark,我可以看到从Linux机器发送的数据报在没有受到伤害的情况下到达Windows机器上。但是UDP客户端接收调用抛出和异常说:“无法完成非阻塞套接字操作 立即“根据我的理解是一个WSAEWOULDBLOCK错误。但是我明确地将阻止选项设置为false。
事件的顺序如下:
Windows机器在端口2上发送数据报并侦听端口1上的确认。我有一个实现超时的while循环
代码:
DateTime TimeAtStart = new DateTime();
TimeAtStart = DateTime.Now;
TimeSpan TimeOut = new TimeSpan(0,0,0,0,800);
IntPtr RecievedTelPtr = new IntPtr();
bool UnpackingResult;
while (TimeOut > (DateTime.Now - TimeAtStart))
{
if (!NackAckRecieveConnection.GetIsReady())
{
ErrorEventArguements.SetAllHmiNetworkEventArgs(ID, -3, 2);
return (false);
}
if (NackAckRecieveConnection.NewMessageReceived())
{
RecievedTelPtr = NackAckRecieveConnection.GetMessage();
UnpackingResult = UnpackHmiTelegram(RecievedTelPtr, AckNackType);
NackAckRecieveConnection.MessageRetrieved();
return (UnpackingResult);
}
}
//if escape loop return timeout err msg
ErrorEventArguements.SetAllHmiNetworkEventArgs(ID, -4, (AckNackType == 0) ? (1) : (3));
return (false);
我希望能够理解问题以及问题发生的原因,以及如何解决问题,因为我很开心。
谢谢
答案 0 :(得分:2)
我没有回答这个问题,但我确实需要指出一些非常重要的事情:
catch (Exception)
{
this._isReady = false;
return this._isReady;
}
NOT 隐藏此类异常。当一些事情失败时你就没有机会尝试修复它,因为你永远不会知道为什么失败了。请使用适当的异常处理。
由于应用程序的原因,我需要快速,非阻塞的UDP套接字操作
该陈述不正确。非阻塞套接字不会更快,它们只是在操作完成之前返回。
我建议您切换回阻塞套接字,因为您似乎是套接字编程的新手。首先运行应用程序,然后尝试优化它。
答案 1 :(得分:0)
您正在将正在阅读邮件的套接字设置为非阻止。如果无法立即完成操作,则指示套接字 NOT BLOCK 。实际上这意味着如果你试图从套接字读取并且没有什么等待被读取,那么呼叫将不会成功返回。
我不知道如何调用MessageReceived
,但是我会假设调用它的任何内容都不是在调用之前检查信息是否已准备好从套接字中读取。
当您遇到间歇性问题时,它会建议大多数情况下,在调用MessageReceived
时,都会从套接字中读取数据。
如果您想继续使用非阻塞IO,您需要更改逻辑,以便捕获IO异常并在短暂延迟后重试(如果您确定那里会有数据),或者在尝试执行读取之前检查是否确实存在可从套接字读取的数据。
检查套接字上是否有可用信息(尝试从中读取之前)的一种方法是使用Socket.Poll。类似的东西:
if (_myUDPClient.Poll(myTimeoutInMicroSeconds, SelectMode.SelectRead)){
// Try to read the new messsage
} else {
continue; // go back to top of loop and try again
}
您可能还需要检查SelectError
状态,以确定套接字是否出现故障。我的大多数套接字编程都来自C ++,所以我不确定.Net细节。