我正在编写一个TCP服务器来打开一个端口,并与一些只将字符串数据作为字节数组发送的硬件进行通信。
环境处于统一状态,所以我使用异步回调来避免阻塞程序。这似乎工作正常,连接是有效的,正确的数据出现,我可以发送消息到硬件,但套接字缓冲区永远不会清除。当我做Socket.EndReceive(ar)时,数据只是堆积在一起,而不是空出来。
异步循环是如何工作的?我不明白为什么这段代码没有完成循环和清除缓冲区。我花了相当长的时间来理解这个过程,并且不知道为什么这段代码不能工作。
protected TcpListener ListenServer;
protected Socket SimNetSocket;
protected byte[] ReadBuffer = new byte[1024];
protected string MessageBuffer;……
public void BeginReceive()
{
SimNetSocket.BeginReceive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None, EndReceive, null);
}
protected void EndReceive(IAsyncResult async)
{
string msg = "";
try { msg = ByteArrayToString(ReadBuffer); }
catch (Exception e) { Debug.LogError(e); }
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
ReadBuffer = new byte[1024];
SimNetSocket.EndReceive(async);
BeginReceive();
}MessageBuffer是一个堆栈,稍后在更新循环中清除,其中消息被处理,并且与套接字上的ReadBuffer复合问题无关。
同样,连接是有效的(代码未显示),并且通信肯定是有效的。我可以看到数据成功地进出双方,但我没有任何控制的另一端硬件正在做什么。是否需要一些实现来接收这些呼叫并确认缓冲区可以清除?
我实际上看到的是硬件发送一条消息,然后是另一条被堆叠在最后一条消息上的消息,一次又一次,一次又一次。不过,我正在通过上面的代码处理每条消息。所以我很困惑。
发布于 2017-06-12 21:19:12
还不清楚你预期会发生什么。Winsock API中和该API之上的瘦.NET层都不会“清除”您提供的任何缓冲区。API所做的就是将读操作中的字节复制到缓冲区中,或者从缓冲区复制用于写操作的字节。
查看您的EndReceive()回调,您似乎误解了其中的一些方面。在完成读取操作(通过调用ReadBuffer )之前,您正在处理EndReceive()内容,并且没有考虑实际接收的字节数。如果一开始没有一个好的Minimal, Complete, and Verifiable code example,就不可能确切地知道您的代码应该做什么,但是方法的更好的实现如下所示:
protected void EndReceive(IAsyncResult async)
{
try
{
int byteCount = SimNetSocket.EndReceive(async);
// For example (you didn't share ByteArrayToString(), so it's not clear
// what encoding you're using, or if you're even processing the bytes
// correctly. Feel free to modify as needed...just make sure you take
// into account the byteCount value!
string msg = Encoding.ASCII.GetString(ReadBuffer, 0, byteCount);
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
// There is no need to allocate a new buffer. Just reuse the one you had
BeginReceive();
}
catch (IOException e)
{
// Don't catch all exceptions. Only exceptions that should be expected
// here would be IOException. Other unexpected exceptions should be left
// unhandled
Debug.LogError(e);
// You should close the socket here. Don't try to use that connection again
}
}请注意,实际上您可以处理ASCII以外的编码,而不必担心部分读取。要做到这一点,您必须跟踪字符解码状态从一个读操作到下一个。最简单的方法是使用Decoder对象,该对象具有内部缓冲区,它将保留部分字符,直到执行下一次解码操作为止。
发布于 2017-06-12 21:14:07
仅仅因为您要求读取ReadBuffer.Length字节,并不意味着实际填充到缓冲区的字节数。您需要保留从int返回的EndReceive,并且只从缓冲区读取这#字节。
public void BeginReceive()
{
SimNetSocket.BeginReceive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None, EndReceive, null);
}
protected void EndReceive(IAsyncResult async)
{
string msg = "";
int bytesRead = SimNetSocket.EndReceive(async);
try { msg = ByteArrayToString(ReadBuffer,bytesRead); }
catch (Exception e) { Debug.LogError(e); }
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
//ReadBuffer = new byte[1024]; //Not necessary, you can re-use the old buffer.
BeginReceive();
}发布于 2017-06-12 21:15:41
您完全忽略了EndReceive结果,它将告诉您已经接收了多少字节。
这样更改您的EndReceive:
protected void EndReceive(IAsyncResult async)
{
string msg = "";
try
{
int received = SimNetSocket.EndReceive(async);
var tmpArr = new byte[received];
Buffer.BlockCopy(ReadBuffer, 0, tmpArr, 0, received);
msg = ByteArrayToString(tmpArr);
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
BeginReceive();
}
catch (Exception e) { Debug.LogError(e); }
}有一些优化需要做,但我无法编写它们,因为我没有完整的代码:
-Modify ByteArrayToString以避免创建临时数组。
-If当您执行SimNetSocket.EndReceive(async)时会抛出一个异常--它意味着连接已经关闭,这将是处理这种情况的一个好主意。
-Beware表示您正在将接收到的数据连接在MessageBuffer上,当您使用该数据时,清空该变量是您的响应。
-You没有考虑阅读零碎命令的可能性(至少在您提供的代码中是这样的)。
https://stackoverflow.com/questions/44508721
复制相似问题