C# 半关闭TCP客户端

C# 半关闭TCP客户端,c#,.net,tcpclient,C#,.net,Tcpclient,我对关闭TCP客户端有严重的问题。我想做的是: 在客户机上: 发送消息 关闭基础套接字以进行发送 收到答复 关闭底层套接字进行读取(或者,此时,只需关闭它) 在服务器上: 收到消息 关闭底层套接字以进行读取 发送响应 关闭底层套接字以进行写入(或者,此时,只需关闭它) 但是,在客户端或服务器上执行步骤2之后,我无法使用TcpClient的流。 下面是我的代码的一个非常简化的版本(没有异步调用、处理和清理,也没有使用StreamReader和StreamWriter而不是XmlSerializer

我对关闭TCP客户端有严重的问题。我想做的是:

在客户机上:

  • 发送消息
  • 关闭基础套接字以进行发送
  • 收到答复
  • 关闭底层套接字进行读取(或者,此时,只需关闭它)
  • 在服务器上:

  • 收到消息
  • 关闭底层套接字以进行读取
  • 发送响应
  • 关闭底层套接字以进行写入(或者,此时,只需关闭它)
  • 但是,在客户端或服务器上执行步骤2之后,我无法使用TcpClient的流。 下面是我的代码的一个非常简化的版本(没有异步调用、处理和清理,也没有使用StreamReader和StreamWriter而不是XmlSerializer):


    有没有办法不用原始套接字就可以做到这一点?有什么地方我弄错了吗?

    问题是
    ReadToEnd
    读取数据直到流的末尾。通过发出
    Client.Shutdown
    实际上关闭了套接字,使其无法重用(至少在
    TCPClient
    的情况下)。下面是
    GetStream()的代码

    如您所见,错误是由于套接字关闭而发生的


    编辑:这很奇怪,但我想我找到了它不能正常工作的原因。原因是
    Shutdown
    总是将整个套接字的标志设置为断开连接。即使我们实际上并没有这样结束它!如果我们在方法开始时保留流,我们将不会遇到这个问题,因为问题在于检查套接字状态的
    GetStream
    方法。但当其他代码检查套接字的状态时,我们可能会遇到一些错误。

    失败是什么意思?跳转到原始套接字似乎是不成熟和武断的。更确切地说,修复错误,您就可以了。
    StreamWriter
    拥有传递给它的流的所有权。当它被垃圾收集时,底层流也会被处理。如果您按照预期的方式处理了
    StreamWriter
    (因为它是
    IDisposable
    ),您就会清楚地看到这一点。您可以使用使流保持打开状态的构造函数重载。或者,不必为此费心——如果您需要访问
    TcpClient
    的底层套接字,那么您通常是做错了。实际上,不需要使用正确的中止逻辑手动关闭套接字。@JeroenMostert捕捉得很好,但问题不应该轻易显现出来。SR有终结器吗?不应该。此外,关闭至远程侧的can信号流完成。我想关闭receiving没有什么作用,是的。@Jeroenmoster在我使用的实际代码中使用了一个XmlSerializer,它没有实现IDisposable,问题仍然存在。我使用StreamWriter和StreamReader很容易说明我的问题,不过也许我应该离开XmlSerializer。@usr在代码中添加了关于异常的注释。至于关闭接收,这可能是不必要的,但我需要关闭发送,以向另一方发出信号,表明我已完成数据传输(连接需要与假设我在完成后关闭发送的实现兼容)。因此,不可能对TcpClient执行半关闭,那么?太遗憾了。有可能半关闭一个连接。悬而未决的问题是:如果代码看起来是半关闭的,为什么套接字是完全关闭的。另外,您可以在开始时只调用一次GetStream,这将使代码更加清晰@Noctiphobia请这样做并更新代码。@Noctiphobia好的,我已经重写了代码,看起来我可以毫无问题地来回发送数据。无论如何,如果您想将套接字重新用于双向通信,那么显式处理套接字会更安全,我说这是定义的预期行为。我一直认为TCP连接总是意味着两个开放流,每个方向一个。即使我不发送回一个字节,只读取/使用/确认传入字节,返回连接也必须存在,并且从我的代码角度来看是“向上”的。至少我一直这么想。这就是为什么我对最初的问题感到震惊,因为从定义上讲,这是不可能的,即使你可以使用垃圾收集技巧来实现它。就我的两块钱。
            //initialize the connection between the server and the client
            var listener = new TcpListener(IPAddress.Any, 13546);
            listener.Start();
            var client = new TcpClient("127.0.0.1", 13546);
            var server = listener.AcceptTcpClient();
            listener.Stop();
    
            //CLIENT: send the message
            var cwriter = new StreamWriter(client.GetStream());
    
            cwriter.Write("client's message");
            cwriter.Flush();
    
            client.Client.Shutdown(SocketShutdown.Send);
    
            //SERVER: receive the message
            string msg;
            var sreader = new StreamReader(server.GetStream());
    
            msg = sreader.ReadToEnd();
    
            server.Client.Shutdown(SocketShutdown.Receive);
    
            //SERVER: send a response
            //Here the code fails on server.GetStream() - 
            //InvalidOperationException, apparently the whole connection is closed now
    
            var swriter = new StreamWriter(server.GetStream());
    
            swriter.Write(msg + " with server's response");
            swriter.Flush();
    
            server.Client.Shutdown(SocketShutdown.Send);
    
            //CLIENT: receive the message
            var creader = new StreamReader(client.GetStream());
    
            var response = creader.ReadToEnd();
    
            client.Client.Shutdown(SocketShutdown.Receive);
    
    public NetworkStream GetStream() {
        if(Logging.On)Logging.Enter(Logging.Sockets, this, "GetStream", "");
        if (m_CleanedUp){
            throw new ObjectDisposedException(this.GetType().FullName);        
        }
        if (!Client.Connected) {
            throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
        }
        if (m_DataStream==null) {
            m_DataStream = new NetworkStream(Client, true);
        }
        if(Logging.On)Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream);
        return m_DataStream;
    }