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



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

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

服务契约定义如下:

[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);
}

Update:我已经重新排序了这一点,使它更容易让人快速阅读问题,而不必阅读我的描述的其余部分,这很长。显示如何启动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
            }
        });
}

异常错误是:"error:读取流时抛出异常。"堆栈跟踪:

   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的AutoDisposeParameter,在操作结束时处置所有可处置的参数。所以我希望在您的Task能够处理整个流之前操作结束。

问题本质上是我通过调用Task.Factory.StartNew而不是确保我的服务通过ServiceBehaviorAttribute正确配置并发性来进行不必要的并发性。感谢Ladislav为我指明了正确的方向。

相关内容

  • 没有找到相关文章

最新更新