我用C#编写了一个小型UDP客户端服务器类,用于在Linux和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连接来简化发送和接收调用。
问题出现在我尝试从套接字中读取数据时。
更多代码:
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客户端Receive调用会抛出异常,错误信息为:"A non-blocking socket operation could not be completed immediately",我理解这是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);
我希望能够理解问题所在,为什么会出现这个问题,以及如何修复它,因为我已经没有任何创意了。谢谢。