c# SocketAsyncEventArgs不发送所有数据

本文关键字:数据 SocketAsyncEventArgs | 更新日期: 2023-09-27 18:05:47

我有一个问题与SocketAsyncEventArgs类..问题是当我试图发送8K的数据例如,在互联网上,套接字有时只发送1K或2K,我知道这是正常的TCP套接字,一次发送不能保证一次接收。现在,为了让它为我工作,我修改了代码以重新发送剩余的数据,例如当我调用SocketAsyncEventArgs。SendAsync完成,在回调中我检查它是否发送了所有的8K如果不是,我调用SocketAsyncEventArgs。SendAsync再次与剩余的数据直到我全部发了。现在,当我看一些SocketAsyncEventArgs代码…我看到大多数人都不这样做!它们只是在发送完成后进行清理,而不检查是否发送了所有数据!当我看微软的例子时,他们说的是对SocketAsyncEventArgs的一次调用。SendAsync保证所有的数据都将被发送。我的意思是我测试了它自己,没有所有的数据将不会在一个调用SocketAsyncEventArgs.SendAsync发送。我做错了什么?提前谢谢。

编辑:这里是不发送所有数据的代码(完全像微软的)当套接字发送1Kb的数据时,SendAsyncComplete将被调用不是所有的8K!

public virtual void Send(byte[] packet, int offset, int length)
{
    if (_tcpSock != null && _tcpSock.Connected)
    {
        var args = SocketHelpers.AcquireSocketArg();
        if (args != null)
        {
            args.Completed += SendAsyncComplete;
            args.SetBuffer(packet, offset, length);
            args.UserToken = this;
            var willRaiseEvent = _tcpSock.SendAsync(args);
            if (!willRaiseEvent)
            {
            ProcessSend(args);
            }
            unchecked
            {
                _bytesSent += (uint)length;
            }
            Interlocked.Add(ref _totalBytesSent, length);
        }
        else
        {
            log.Error("Client {0}'s SocketArgs are null", this);
        }
    }
}
private static void ProcessSend(SocketAsyncEventArgs args)
{
      args.Completed -= SendAsyncComplete;
      SocketHelpers.ReleaseSocketArg(args);   
}
private static void SendAsyncComplete(object sender, SocketAsyncEventArgs args)
{
    ProcessSend(args);
}

c# SocketAsyncEventArgs不发送所有数据

我有很多地方要改。作为序言,请阅读以下内容。

首先,当你在套接字上发送任何数据时,你必须处理该事件:要么停止整个发送过程,要么发出另一个套接字发送操作来发送剩余的数据。

所以有三个方法是有意义的,像这样:
// This is the Send() to be used by your class' clients
1) public void Send(byte[] buffer);

这个方法将注意应用你需要的任何数据格式,创建(检索)一个SocketAsyncEventArgs对象,设置令牌来保存你的缓冲区,并调用下面的下一个方法:

2) private void Send(SocketAsyncEventArgs e);

这个调用套接字。非同步(SocketAsyncEventArgs e)并将内容从令牌(缓冲区,记得吗?)复制到SAEA对象。这就是为什么,因为方法号(2)可能会被调用多次,以发送套接字无法在一次操作中发送的剩余数据。因此,这里您将令牌中的剩余数据复制到SAEA缓冲区。

3) private void ProcessSent(SocketAsyncEventArgs e);

最后一个方法将检查套接字发送的数据。如果所有数据都已发送,则将释放SAEA对象。如果没有,将对其余数据再次调用方法(2)。为了跟踪发送的数据,您可以使用saea . bytestrered。你应该将这个值添加到存储在我建议你创建的自定义令牌中的值中(所以不要使用"this"作为令牌)。

这也是您在SAEA参数上检查SocketError的地方。

最后一个方法将在两个地方被调用:

  • 在第二个方法中,像这样:

        // Attempt to send data in an asynchronous fashion
        bool isAsync = this.Socket.SendAsync(e);
        // Something went wrong and we didn't send the data async
        if (!isAsync)
            this.ProcessSent(e);
    

这一点很重要,许多人甚至在使用更传统的Begin/EndXXX模式(在这种情况下,通过IAsyncResult)时都忽略了它。如果你不放置它,偶尔(相当罕见),StackOverflow异常会突然冒出来,让你困惑很长一段时间。

    完成事件处理程序中的
  • :

    private void Completed(object sender, SocketAsyncEventArgs e)
    {
        // What type of operation did just completed?
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Send:
                {
                    ProcessSent(e);
                    break;
                }
        }
    }
    

棘手的事情是在第一次Send(byte[])操作中使用一个SocketAsyncEventArgs对象,如果所有数据都已发送,则在第三次操作中释放它。

为此,您必须创建一个自定义令牌(类或不可变结构)来放置在SocketAsyncEventArgs中。UserToken,然后跟踪在每个Socket.SendAsync()操作上传输了多少数据。

当您阅读开头提供的文章时,请注意在Send()操作结束时,如果所有数据都已发送,那么作者是如何重用相同的SAEA对象的。这是因为他的协议是:每一方(服务器和客户端)轮流相互通信。

现在,如果对第一个Send()方法的多个调用同时发生,那么操作系统处理它们的顺序是没有规则的。如果这种情况很可能发生并且消息顺序很重要,并且由于任何来自"外部实体"的Send(byte[])调用都会导致Socket.SendAsync(),我建议第一个方法实际上在内部缓冲区中写入接收到的字节。只要这个缓冲区不是空的,就继续在内部发送这个数据。可以将其视为生产者-消费者场景,其中"外部实体"是生产者,而内部发送op是消费者。我更喜欢乐观的并发场景。

除了本文之外,关于这个问题的文档相当肤浅,即使在这种情况下,当您开始实现自己的应用程序时,也会发现有些事情是不同的。这个SocketAsyncEventArgs模型可能有点违反直觉。

让我知道如果你需要更多的帮助,我有我的时间与此斗争,当我开发我自己的库。

编辑:

如果我是你,我就搬家

unchecked
{
    _bytesSent += (uint)length;
}
Interlocked.Add(ref _totalBytesSent, length);

到ProcessSend(SAEA),并使用参数。