首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#如何手动清除TCP缓冲区?

C#如何手动清除TCP缓冲区?
EN

Stack Overflow用户
提问于 2017-06-12 21:06:55
回答 4查看 14.8K关注 0票数 1

我正在编写一个TCP服务器来打开一个端口,并与一些只将字符串数据作为字节数组发送的硬件进行通信。

环境处于统一状态,所以我使用异步回调来避免阻塞程序。这似乎工作正常,连接是有效的,正确的数据出现,我可以发送消息到硬件,但套接字缓冲区永远不会清除。当我做Socket.EndReceive(ar)时,数据只是堆积在一起,而不是空出来。

异步循环是如何工作的?我不明白为什么这段代码没有完成循环和清除缓冲区。我花了相当长的时间来理解这个过程,并且不知道为什么这段代码不能工作。

代码语言:javascript
复制
    protected TcpListener ListenServer;
    protected Socket SimNetSocket;
    protected byte[] ReadBuffer = new byte[1024];
    protected string MessageBuffer;

……

代码语言:javascript
复制
    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复合问题无关。

同样,连接是有效的(代码未显示),并且通信肯定是有效的。我可以看到数据成功地进出双方,但我没有任何控制的另一端硬件正在做什么。是否需要一些实现来接收这些呼叫并确认缓冲区可以清除?

我实际上看到的是硬件发送一条消息,然后是另一条被堆叠在最后一条消息上的消息,一次又一次,一次又一次。不过,我正在通过上面的代码处理每条消息。所以我很困惑。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-06-12 21:19:12

还不清楚你预期会发生什么。Winsock API中和该API之上的瘦.NET层都不会“清除”您提供的任何缓冲区。API所做的就是将读操作中的字节复制到缓冲区中,或者从缓冲区复制用于写操作的字节。

查看您的EndReceive()回调,您似乎误解了其中的一些方面。在完成读取操作(通过调用ReadBuffer )之前,您正在处理EndReceive()内容,并且没有考虑实际接收的字节数。如果一开始没有一个好的Minimal, Complete, and Verifiable code example,就不可能确切地知道您的代码应该做什么,但是方法的更好的实现如下所示:

代码语言:javascript
复制
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对象,该对象具有内部缓冲区,它将保留部分字符,直到执行下一次解码操作为止。

票数 0
EN

Stack Overflow用户

发布于 2017-06-12 21:14:07

仅仅因为您要求读取ReadBuffer.Length字节,并不意味着实际填充到缓冲区的字节数。您需要保留从int返回的EndReceive,并且只从缓冲区读取这#字节。

代码语言:javascript
复制
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();
}
票数 2
EN

Stack Overflow用户

发布于 2017-06-12 21:15:41

您完全忽略了EndReceive结果,它将告诉您已经接收了多少字节。

这样更改您的EndReceive:

代码语言:javascript
复制
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没有考虑阅读零碎命令的可能性(至少在您提供的代码中是这样的)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44508721

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档