草庐IT

c# - 客户端关闭连接时在服务器上多次调用 NetworkStream 回调

coder 2023-09-19 原文

我正在编写一个模拟器来测试对象的(反)序列化并通过 TCP 发送它们。 我在 C# 中使用 TcpClient、TcpListener 和 NetworkStreams 进行通信。 我想打开一个连接,发送多条消息并在一段时间后关闭连接。 建立连接并双向发送数据包工作正常。 然而,当我关闭客户端上的连接(调用 stream.Close()tcpClient.Close())时,服务器对 BeginRead 的回调会被一次又一次地调用。

客户端代码:

private TcpClient client;

public void StartClient()
{
    this.client = new TcpClient(this.serverAddress, this.port);
}

public void Send(byte[] data)
{
    var stream = this.client.GetStream();
    stream.Write(data, 0, data.Length);
}

public void StopClient()
{
    this.client.GetStream().Close();
    this.tcpClient.Close();
}

服务器代码:

private readonly AutoResetEvent autoResetEvent= new AutoResetEvent(false);

private TcpListener tcpListener;

public event Action<byte[]> MessageReceived;

public void StartListening()
{
    this.tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1337);
    this.tcpListener.Start();
    this.tcpListener.BeginAcceptTcpClient(this.ClientConnecting, this.tcpListener);
}

private void ClientConnecting(IAsyncResult result)
{
    var listener = (TcpListener)result.AsyncState;

    this.tcpClient = listener.EndAcceptTcpClient(result);
    this.Listen();
}

private void Listen()
{
    var stream = this.tcpClient.GetStream();
    var buffer = new byte[256];
    while (stream.CanRead)
    {
        try
        {
            stream.BeginRead(buffer, 0, buffer.Length, this.DataReceiveCallback, new ReceiveState { Buffer = buffer, Stream = stream });
        }
        catch
        {
            this.StopListening();
            break;
        }

        this.autoResetEvent.WaitOne();
    }
}

public void StopListening()
{
    var stream = this.tcpClient.GetStream();
    stream.Close();

    this.tcpClient.Close();
    this.tcpClient = null;
}

private void DataReceiveCallback(IAsyncResult ar)
{
    var state = (ReceiveState)ar.AsyncState;
    var stream = state.Stream;

    if (stream.CanRead)
    {
        var buffer = state.Buffer;
        var numberOfBytesRead = stream.EndRead(ar);

        if (this.MessageReceived != null)
        {
            if (!stream.DataAvailable)
            {
                this.MessageReceived(buffer.Take(numberOfBytesRead).ToArray());
            }
            else
            {
                var receivedBytes = new List<byte>(buffer);
                while (stream.DataAvailable)
                {
                    numberOfBytesRead = stream.Read(buffer, 0, buffer.Length);
                    receivedBytes.AddRange(buffer.Take(numberOfBytesRead));
                }
                this.MessageReceived(receivedBytes.ToArray());
            }
        }
    }

    this.autoResetEvent.Set();
}

当我在客户端调用 StopClient() 时,将调用服务器上的回调 (DataReceiveCallback)。 stream.CanRead 返回 true,但有 0 个字节要读取。此外,stream.DataAvailable 为 false。

但是,除此之外,我看不出有什么办法可以在服务器端知道客户端关闭了连接。并且当连接仍然打开时,有可能从客户端接收到 0 个字节。

现在发生的事情是,在客户端关闭连接后,DataReceiveCallback 被一次又一次地调用,总是读取 0 字节。

我想做的是在客户端关闭连接时关闭服务器上的连接。我该怎么做才能实现这一目标?

注意:我知道这个例子缺乏正确的错误处理,你不应该在你读到一些东西后开始同步阅读,直到 stream.Read() 返回 0。但是,这适合我现在的需要,因为我直接控制所有正在发送的数据。

最佳答案

您正在循环调用 BeginRead。如果流完成,读取立即完成并返回 0 字节并继续循环。

您使用异步 IO 毫无意义,因为您正在阻塞等待 IO 完成的线程。毫无疑问,您是从错误的 MSDN 示例中复制的(实际上并没有理解它的作用)。

扔掉它并使用普通循环同步读取。当 read 调用返回 0 时,您将中断循环。

另请注意,TCP 不以您现在假设的任何方式支持消息。删除 CanReadDataAvailable 的所有用法。只要您考虑这些属性,您就很可能做错了。

关于c# - 客户端关闭连接时在服务器上多次调用 NetworkStream 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31022971/

有关c# - 客户端关闭连接时在服务器上多次调用 NetworkStream 回调的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  4. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  5. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  6. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  7. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  8. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  9. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  10. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

    您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

随机推荐