c# - WCF 服务,由于传入流的 "Unexpected end of file"引发流异常

标签 c# wcf stream

我编写了一个交换传输代理,用于将电子邮件正文上传到 WCF 服务。该服务与 Exchange 位于同一台机器上,监听 localhost:1530 以接收传入的 TCP 连接。传输代理是使用 .NET 3.5 Framework 实现的,该服务在 .NET 4.0 框架上实现的 Windows 服务中自托管。

我的问题:为什么流在读取完成之前终止,如何修复它?

服务契约(Contract)定义如下:

[ServiceContract]
public interface IReceiveService
{
    [OperationContract]
    Guid ImportMessage(DateTime dateTimeReceived, string from, IList<string> to, string subject, int attachmentCount, bool bodyEmpty, Guid clientID);

    [OperationContract]
    void ImportMessageBody(IMessageBody body);

    [OperationContract]
    void ImportMessageAttachment(IMessageAttachment attachment);
}

更新:我对此进行了重新排序,以便人们可以更轻松地快速阅读问题,而不必阅读我的描述的其余部分(该描述很长)。显示我如何启动 Task 来处理请求的第一部分似乎是问题所在。事实上,如果我注释掉 Task.Factory.StartNew() 部分,Stream.CopyTo 就可以工作。

在我的服务实现中,我尝试使用 Stream.CopyTo 将传入流复制到临时文件,如下所示:

public void ImportMessageBody(IMessageBody body)
{
    Task.Factory.StartNew(() =>
        {
            string fileName = GetFileNameFromMagicalPlace();
            using (FileStream fs = new FileStream(fileName, FileMode.Append))
            {
                body.Data.CopyTo(fs); // <---- throws System.IOException right here
            }
        });
}

异常错误为:“错误:读取流时抛出异常。”堆栈跟踪:

   at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Read(Byte[] buffer, Int32 offset, Int32 count) 
   at System.IO.Stream.CopyTo...

有一个内部异常:

System.Xml.XmlException: Unexpected end of file. Following elements are not closed: Address, IMessageBody, Body, Envelope. 
    at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3) 
    at System.Xml.XmlBufferReader.GetByteHard() 
    at System.Xml.XmlBufferReader.ReadMultiByteUInt31() 
    at System.Xml.XmlBinaryReader.ReadName(StringHandle handle) 
    at System.Xml.XmlBinaryReader.ReadNode() 
    at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Read(Byte[] buffer, Int32 offset, Int32 count) 

其他详细信息如下。


IMessageBody 定义如下:

[MessageContract]
public class IMessageBody
{
    [MessageHeader]
    public Guid MessageID { get; set; }

    [MessageHeader]
    public string Format { get; set; }

    [MessageBodyMember]
    public System.IO.Stream Data { get; set; }
}

显示相关位的传输代理的缩写版本(我希望):

public class Agent : RoutingAgent
{
    public delegate void PostingDelegate(MailItem item);
    IReceiveService service;    

    public Agent()
    {
        string tcpServiceUri = "net.tcp://localhost:1530";

        NetTcpBinding endpointBinding = new NetTcpBinding();
        endpointBinding.TransferMode = TransferMode.Streamed;

        ServiceEndpoint serviceEndpoint = new ServiceEndpoint(
            ContractDescription.GetContract(typeof(IReceiveService)),
            endpointBinding,
            new EndpointAddress(tcpServiceUri));
        ChannelFactory<IReceiveService> factory = new ChannelFactory<IReceiveService>(serviceEndpoint);
        service = factory.CreateChannel();

        this.OnSubmittedMessage += new SubmittedMessageEventHandler(Agent_OnSubmittedMessage);
    }

    void Agent_OnSubmittedMessage(SubmittedMessageEventSource source, QueuedMessageEventArgs e)
    {
        if (TheseAreTheDroidsImLookingFor(e))
        {
            PostingDelegate del = PostData;
            del.BeginInvoke(e.MailItem, CompletePost, GetAgentAsyncContext());
        }
    }

    void PostData(MailItem item)
    {
        // Body class is basically direct implementation of IMessageBody 
        // with a constructor to set up the public properties from MailItem.
        var body = new Body(item); 
        service.ImportMessageBody(body);
    }

    void CompletePost(IAsyncResult ar)
    {
        var context = ar.AsyncState as AgentAsyncContext;
        context.Complete();
    }
}

最后,服务实现的托管方式如下:

string queueUri = String.Format("net.tcp://localhost:{0}/{1}", port, serviceName);
            try
            {
                host = new ServiceHost(typeof(ReceiveService), new Uri(queueUri));
                host.AddDefaultEndpoints();
                var endpoint = host.Description.Endpoints.First();
                ((NetTcpBinding)endpoint.Binding).TransferMode = TransferMode.Streamed;                 

                trace.Log(Level.Debug,String.Format("Created service host: {0}", host));                

                host.Open();                
                trace.Log(Level.Debug,"Opened service host.");
            }
            catch (Exception e)
            {
                string message = String.Format("Threw exception while attempting to create ServiceHost for {0}:{1}\n{2}", queueUri, e.Message, e.StackTrace);
                trace.Log(Level.Debug,message);
                trace.Log(Level.Error, message);
            }

最佳答案

代码太多了。您应该尝试将代码最小化为尽可能小的可重现示例。我想问题可能出在这里:

public void ImportMessageBody(IMessageBody body)
{
    Task.Factory.StartNew(() =>
        {
            string fileName = GetFileNameFromMagicalPlace();
            using (FileStream fs = new FileStream(fileName, FileMode.Append))
            {
                body.Data.CopyTo(fs); // <---- throws System.IOException right here
            }
        });
}

默认情况下应用OperationBehavior,此行为包含默认设置为true的AutoDisposeParameterdisposes all disposable parameters当操作结束时。因此,我希望该操作在您的 Task 能够处理整个流之前结束。

关于c# - WCF 服务,由于传入流的 "Unexpected end of file"引发流异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6680558/

相关文章:

c# - 从 C# 中的不同线程使用 Invoke 访问类成员

c# - 通过配置强制在 wcf 客户端 c# 中使用 tls 1.0

java - 检测压缩文件java

wcf - 从其 URI (http ://ip:port/Service1. svc?WSDL) 动态调用 WCF 服务

asp.net - 在 ASP.Net 应用程序和最佳实践中使用 WCF

C++流式传输到char数组?

javascript - Koa中如何发送生成的文件

c# - WEB API 路由错误发现多个操作与具有不同 API 路径的请求相匹配

javascript - Linkbutton 的奇怪行为

c# - 如何在 javascript 中获取 UTC 偏移量(类似于 C# 中的 TimeZoneInfo.GetUtcOffset)