无法通过 WCF 将流式数据从客户端发送到服务器
Can't send streamed data from client to server via WCF
我正在尝试使用 WCF 中的流功能将一些数据从客户端发送到服务器。我可以毫无问题地读取从服务器返回的流。然而,其他方式不起作用。
尝试在 class 中用 MessageContract 装饰包装流,但没有成功。
客户端配置:
<bindings>
<netTcpBinding>
<binding name="streamingBinding" transferMode="Streamed"
maxReceivedMessageSize="5000000000">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
服务器配置:
<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>
宿主应用:
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();
}
客户端应用程序:
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();
}
服务:
[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;
}
}
频道工厂实现:
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;
}
}
请在创建文件流的行填写文件名。
在 运行 主机和客户端之后,在客户端 window 中按回车键,然后在主机 window 中抛出异常:
System.ObjectDisposedException: 无法访问已关闭的流。 (在主机应用程序中在线 'stream.CopyTo(file);')
反向场景工作正常(将文件从服务器发送到客户端)
我建议您使用异步模型 build/implement 服务合同,因为文件流并不总是同步的。请参考以下代码段。
[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);
}
}
}
}
调用。
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();
如果有什么我可以帮忙的,请随时告诉我。
我知道是什么导致了我的问题。问题在这里:
public Stream GetData()
{
return _data;
}
从 GetData 方法返回后,WCF 自动关闭流。要在服务 class 之外传播流,我必须使用一个事件:
public event Action<StreamMessage> DataSet;
public void SetData(StreamMessage data)
{
_data = data;
DataSet?.Invoke(data);
}
然后在事件处理程序中使用流。
我正在尝试使用 WCF 中的流功能将一些数据从客户端发送到服务器。我可以毫无问题地读取从服务器返回的流。然而,其他方式不起作用。
尝试在 class 中用 MessageContract 装饰包装流,但没有成功。
客户端配置:
<bindings>
<netTcpBinding>
<binding name="streamingBinding" transferMode="Streamed"
maxReceivedMessageSize="5000000000">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
服务器配置:
<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>
宿主应用:
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();
}
客户端应用程序:
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();
}
服务:
[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;
}
}
频道工厂实现:
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;
}
}
请在创建文件流的行填写文件名。
在 运行 主机和客户端之后,在客户端 window 中按回车键,然后在主机 window 中抛出异常:
System.ObjectDisposedException: 无法访问已关闭的流。 (在主机应用程序中在线 'stream.CopyTo(file);')
反向场景工作正常(将文件从服务器发送到客户端)
我建议您使用异步模型 build/implement 服务合同,因为文件流并不总是同步的。请参考以下代码段。
[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);
}
}
}
}
调用。
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();
如果有什么我可以帮忙的,请随时告诉我。
我知道是什么导致了我的问题。问题在这里:
public Stream GetData()
{
return _data;
}
从 GetData 方法返回后,WCF 自动关闭流。要在服务 class 之外传播流,我必须使用一个事件:
public event Action<StreamMessage> DataSet;
public void SetData(StreamMessage data)
{
_data = data;
DataSet?.Invoke(data);
}
然后在事件处理程序中使用流。