处置 XmlWriter 而不发送未完成的结束元素

Posted

技术标签:

【中文标题】处置 XmlWriter 而不发送未完成的结束元素【英文标题】:Dispose of an XmlWriter without sending outstanding end elements 【发布时间】:2010-11-17 11:17:33 【问题描述】:

我正在使用XmlWriter 发送 XMPP (Jingle) 流。 XMPP 协议在协商 TLS 时替换了 XML 流,这意味着 XML 最终如下:

<stream>
 <features>
  ...
 </features>

 ...TLS gets negotiated...

<stream>
 ...
</stream>

XML 最终格式不正确,因为有两个流开始标记。

我想要做的是扔掉我在 TLS 协商之前使用的 XmlWriter 并创建一个全新的,它可以让我更好地模块化我的代码。但是,当我调用 myXmlWriter.Close() 以确保它被处理时,它将发送打破 XMPP 协议的关闭流结束元素。

无论如何我可以关闭XmlWriter 而不发送未完成的结束元素吗?

【问题讨论】:

【参考方案1】:

创建一个中间流,您可以使用它来断开 XmlWriter 与基本流的连接。

这不是最优雅的解决方案,下面的代码需要工作,所以在将其投入生产之前对其进行测试,但这是关于想法的。

public class DummyStream : Stream

    public DummyStream(Stream baseStream)
    
        if (baseStream == null)
            throw new ArgumentNullException("baseStream");

        BaseStream = baseStream;
    

    public Stream BaseStream  get; private set; 

    public void DisconnectBaseStream()
    
        BaseStream = null;
    

    private Stream GetBaseStream()
    
        return BaseStream ?? Stream.Null;
    

    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
    
        return GetBaseStream().BeginRead(buffer, offset, count, callback, state);
    

    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
    
        return GetBaseStream().BeginWrite(buffer, offset, count, callback, state);
    

    public override bool CanRead
    
        get  return GetBaseStream().CanRead; 
    

    public override bool CanSeek
    
        get  return GetBaseStream().CanSeek; 
    

    public override bool CanTimeout
    
        get  return GetBaseStream().CanTimeout; 
    

    public override bool CanWrite
    
        get  return GetBaseStream().CanWrite; 
    

    public override void Close()
    
        // We do not close the BaseStream because this stream
        // is just a wrapper.

        // GetBaseStream().Close();
    

    public override ObjRef CreateObjRef(Type requestedType)
    
        return GetBaseStream().CreateObjRef(requestedType);
    

    protected override void Dispose(bool disposing)
    
        base.Dispose(disposing);

        // We do not dispose the BaseStream because this stream
        // is just a wrapper.
    

    public override int EndRead(IAsyncResult asyncResult)
    
        return GetBaseStream().EndRead(asyncResult);
    

    public override void EndWrite(IAsyncResult asyncResult)
    
        GetBaseStream().EndWrite(asyncResult);
    

    public override bool Equals(object obj)
    
        return GetBaseStream().Equals(obj);
    

    public override void Flush()
    
        GetBaseStream().Flush();
    

    public override int GetHashCode()
    
        return GetBaseStream().GetHashCode();
    

    public override object InitializeLifetimeService()
    
        return GetBaseStream().InitializeLifetimeService();
    

    public override long Length
    
        get  return GetBaseStream().Length; 
    

    public override long Position
    
        get  return GetBaseStream().Position; 
        set  GetBaseStream().Position = value; 
    

    public override int Read(byte[] buffer, int offset, int count)
    
        return GetBaseStream().Read(buffer, offset, count);
    

    public override int ReadByte()
    
        return GetBaseStream().ReadByte();
    

    public override int ReadTimeout
    
        get  return GetBaseStream().ReadTimeout; 
        set  GetBaseStream().ReadTimeout = value; 
    

    public override long Seek(long offset, SeekOrigin origin)
    
        return GetBaseStream().Seek(offset, origin);
    

    public override void SetLength(long value)
    
        GetBaseStream().SetLength(value);
    

    public override string ToString()
    
        return GetBaseStream().ToString();
    

    public override void Write(byte[] buffer, int offset, int count)
    
        GetBaseStream().Write(buffer, offset, count);
    

    public override void WriteByte(byte value)
    
        GetBaseStream().WriteByte(value);
    

    public override int WriteTimeout
    
        get  return GetBaseStream().WriteTimeout; 
        set  GetBaseStream().WriteTimeout = value; 
    

此类旨在用作XmlWriterXmlWriter 输出到的流之间的流。这个类只是将所有来自XmlWriter 的调用转发到基本流,但是一旦你调用DisconnectBaseStream,它就会停止转发它们并且XmlWriter 不能再控制基本流。

你可以像这样使用这个类:

using (var stream = /* stream used to communicate with */)

    using (var wrapperStream = new DummyStream(stream))
    using (var writer = XmlWriter.Create(wrapperStream))
    
        // Do you work here.

        // Now, disconnect the dummy stream so that the XML writer
        // cannot send more data.

        wrapperStream.DisconnectBaseStream();

        // End of the using block will close the XmlWriter and it
        // cannot send more data to the base stream.
    

    // Perform TLS negotiation etc...

同样,DummyStream 是一个起点,需要一些工作。例如,您需要确保XmlWriter 在断开连接后不会拨打电话,这将导致崩溃,因此您需要进行一些检查,例如Write 方法是否 BaseStreamnull 如果是,则跳过调用。

【讨论】:

我其实也想过这样做。我过去使用过自定义 Stream 类。我只是希望有一些不那么笨重的东西,哦。 无论您做什么,XmlWriter 都会尽力输出格式良好的 XML。这是解决这个问题的唯一方法。

以上是关于处置 XmlWriter 而不发送未完成的结束元素的主要内容,如果未能解决你的问题,请参考以下文章

StateSubscriber.Subscribe 未处置

在 XElement WriteTo 方法中,强制 XmlWriter 为具有已定义 xmlns 属性的子元素使用前缀

可以使用 XmlWriter 将 XML 写入内存吗?

说一下 tcp 粘包是怎么产生的?(未完成)

为啥 FxCop 不报告 CA2000 对于这种未处置的类实例的琐碎情况?

将活动发送到后台而不完成