简体   繁体   English

无法通过 WCF 从客户端向服务器发送流数据

[英]Can't send streamed data from client to server via WCF

I'm trying to send some data from client to server using streaming functionality in WCF.我正在尝试使用 WCF 中的流功能将一些数据从客户端发送到服务器。 I can read stream returned from server with no issues.我可以毫无问题地读取从服务器返回的流。 Other way around doesn't work however.然而,其他方式不起作用。

Tried wrapping stream in class decorated with MessageContract with no success.尝试在用 MessageContract 装饰的类中包装流,但没有成功。

Client config:客户端配置:

    <bindings>
      <netTcpBinding>
        <binding name="streamingBinding" transferMode="Streamed" 
    maxReceivedMessageSize="5000000000">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>

Server config:服务器配置:

    <bindings>
      <netTcpBinding>
        <binding name="streamingBinding" transferMode="Streamed" 
    maxReceivedMessageSize="5000000000">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>

... ...

    <service behaviorConfiguration="WcfSvc.WcfServiceBehavior" 
    name="Shared.StreamingService">
        <endpoint address="" binding="netTcpBinding" 
    bindingConfiguration="streamingBinding"
                  contract="Shared.IStreamingService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" 
    bindingConfiguration=""
                  contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8733/StreamingTest/" />
          </baseAddresses>
        </host>
      </service>

Host app:主机应用程序:


    private static IStreamingService _service;
    private static ServiceHost _serviceHost;


    static void Main()
        {
            _service = new StreamingService();
            _serviceHost = new ServiceHost(_service);
            _serviceHost.Open();

            Console.WriteLine("Press enter to read data");
            Console.ReadLine();

            var stream = _service.GetData();
            var file = File.Create(@"PATH TO NON EXISTING FILE");
            stream.CopyTo(file);
            file.Close();

            Console.WriteLine("Press enter to close host");
            Console.ReadLine();

            _serviceHost.Close();
        }

Client app:客户端应用程序:

    private const string EndpointAddress = "net.tcp://localhost:8733/StreamingTest/";
        private const string TcpBindingConfigName = "streamingBinding";

        private static WcfChannelFactory<IStreamingService> _factory = new WcfChannelFactory<IStreamingService>();
        private static IStreamingService _service;
        private static ICommunicationObject _communicationObject;

        static void Main()
        {
            Console.WriteLine("Press enter to connect");
            Console.ReadLine();

            (_service, _communicationObject) = _factory.OpenAsync(EndpointAddress, TcpBindingConfigName).Result;

            var s = File.OpenRead(@"PATH TO EXISTING FILE");
            _service.SetData(s);

            Console.WriteLine("Press enter to disconnect");
            Console.ReadLine();

            _communicationObject.Close();
        }

Service:服务:

    [ServiceContract(SessionMode = SessionMode.NotAllowed)]
    public interface IStreamingService
    {
        [OperationContract]
        void SetData(Stream data);

        [OperationContract]
        Stream GetData();
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class StreamingService : IStreamingService
    {
        private Stream _data;

        public void SetData(Stream data)
        {
            _data = data;
        }

        public Stream GetData()
        {
            return _data;
        }
    }

Channel factory implementation:通道工厂实现:

    public class WcfChannelFactory<TService>
    {
        private ChannelFactory<TService> _channelFactory;

        public async Task<(TService, ICommunicationObject)> OpenAsync(string endpointAddress, string tcpBindingConfigName)
        {
            var tcpBinding = new NetTcpBinding(tcpBindingConfigName);
            _channelFactory = new ChannelFactory<TService>(tcpBinding);
            await Task.Factory.FromAsync(_channelFactory.BeginOpen, _channelFactory.EndOpen, null);
            var wcf = _channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
            return (wcf, wcf as ICommunicationObject);
        }

        public void Close()
        {
            _channelFactory?.Close();
            _channelFactory = null;
        }
    }

Please fill file names at lines where file streams are created.请在创建文件流的行填写文件名。

After running host and client, pressing enter in client window and then in host window exception is thrown:运行主机和客户端后,在客户端窗口按回车,然后在主机窗口抛出异常:

System.ObjectDisposedException: Cannot access a closed Stream. System.ObjectDisposedException:无法访问关闭的流。 (on line 'stream.CopyTo(file);' in host app) (在线'stream.CopyTo(文件);'在主机应用程序中)

Reverse scenario works just fine (sending file from server to client)反向场景工作得很好(从服务器向客户端发送文件)

I know what caused my problem.我知道是什么导致了我的问题。 The problem is here:问题在这里:

public Stream GetData()
{
    return _data;
}

After returning from GetData method WCF automatically closes the stream.从 GetData 方法返回后,WCF 自动关闭流。 To propagate the stream outside of the service class I had to use an event:为了在服务类之外传播流,我必须使用一个事件:

public event Action<StreamMessage> DataSet; 

public void SetData(StreamMessage data)
{
    _data = data;
    DataSet?.Invoke(data);
}

and then consume the stream in the event handler.然后在事件处理程序中使用流。

I suggest you use the Asynchronous model to build/implement the Service contract, since the file stream is not always synchronized.我建议您使用异步模型来构建/实现服务契约,因为文件流并不总是同步的。 Please refer to the below code segments.请参考以下代码段。

    [ServiceContract]
    interface IService
    {
        [OperationContract]
        Task UploadStream(Stream stream);
    }
    public class MyService : IService
    {
        public async Task UploadStream(Stream stream)
        {
            using (stream)
            {
                using (var file = File.Create(Path.Combine(Guid.NewGuid().ToString() + ".png")))
                {
                    await stream.CopyToAsync(file);
                }
            }
        }
}

Invocation.调用。

  ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
        string file = Path.Combine(@"C:\", "1.png");
        FileStream fs = new FileStream(file,FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.ReadWrite);

        //var s = File.OpenRead(file);

        //MemoryStream ms = new MemoryStream();
        //fs.CopyTo(ms);
        //ms.Position = 0;
        client.UploadStream(fs);
        Console.WriteLine("DOne");
        Console.ReadLine();

Feel free to let me know if there is anything I can help with.如果有什么我可以帮忙的,请随时告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM