c# - test - WCF Streaming de datos de gran tamaño(500MB/1GB) en un servicio auto hospedado
wcf c# ejemplo (1)
Actualmente estoy experimentando un problema al intentar enviar datos de gran tamaño mediante el servicio auto hospedado de WCF (sin IIS). Transfiriendo 500MB usando resultados de transmisión con mi servicio fallando con System.OutOfMemoryException. ¿Es posible en absoluto transferir tal cantidad de datos?
Aquí está mi configuración de WCF:
<system.serviceModel>
<services>
<service name="CIService" behaviorConfiguration="CIBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:6547/CIService/CIService.svc" />
</baseAddresses>
</host>
<endpoint binding="netTcpBinding"
bindingConfiguration="netTcpBindingConfig"
behaviorConfiguration="CIBehavior.EndpointBehavior"
contract="CIService.ICreatable" />
<endpoint address="mex"
binding="mexHttpBinding"
name="mexTcpBinding"
contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment multippleSiteBindingEnabled="True" />
<bindings>
<netTcpBinding>
<binding name="netTcpBindingConfig" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="01:00:00" sendTimeout="00:10:00"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxConnections="10"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
transferMode="Streamed">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CIBehavior">
<serviceMetadata httpGetEnabled="False" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="200" maxConcurrentInstances="2147483647" maxConcurrentSessions="100" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
<endpointBehavior>
<behavior name="CIBehavior.EndpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehavior>
</behaviors>
</system.serviceModel>
Mi configuración de cliente:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ICreatable"
closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="01:00:00" sendTimeout="00:10:00"
transactionFlow="false"
transferMode="Streamed"
transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard"
listenBacklog="10"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
maxConnections="10"
maxReceivedMessageSize ="2147483647">
<readerQuotas
maxDepth="2147483647"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" anabled="false" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="NetTcpBinding_ICreatable"
address="net.tcp://localhost:6547/CIService/CIService.svc"
binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ICreatable"
behaviorConfiguration="CIBehavior.EndpointBehavior"
contract="ICreatable" />
</client>
<behaviors>
<endpointBehavior>
<behavior name="CIBehavior.EndpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehavior>
</behaviors>
</system.serviceModel>
No necesita el conjunto maxBufferSize o maxBufferPoolSize tan alto que posiblemente esté causando su excepción de memoria insuficiente. Los valores predeterminados deben estar bien.
Consulte la sección de datos grandes y transmisión por secuencias en MSDN, específicamente la sección Consideraciones especiales de seguridad para datos grandes.
La propiedad MaxBufferSize es necesaria para restringir la memoria que almacena WCF. Es importante establecer esto en un valor seguro (o mantenerlo en el valor predeterminado) cuando se transmite. Por ejemplo, suponga que su servicio debe recibir archivos de hasta 4 GB de tamaño y almacenarlos en el disco local. Supongamos también que su memoria está restringida de tal manera que solo puede almacenar 64 KB de datos a la vez. Luego, establecería MaxReceivedMessageSize en 4 GB y MaxBufferSize en 64 KB. Además, en la implementación de su servicio, debe asegurarse de leer solo el flujo entrante en fragmentos de 64 KB y no leer el siguiente fragmento antes de que el anterior se haya escrito en el disco y se haya descartado de la memoria.
Reuní un ejemplo muy simple de transmisión de datos desde un servicio auto hospedado a un cliente de consola. Para mantener el mensaje corto, solo agregué el código del servidor y parte del cliente.
El contrato de servicio
using System.IO;
using System.ServiceModel;
namespace Service
{
[ServiceContract]
public interface IStream
{
[OperationContract]
Stream GetLargeObject();
}
}
La implementación del servicio.
using System;
using System.IO;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior]
public class StreamService : IStream
{
public Stream GetLargeObject()
{
// Add path to a big file, this one is 2.5 gb
string filePath = Path.Combine(Environment.CurrentDirectory, "C://Temp//BFBC2_PC_Client_R11_795745_Patch.exe");
try
{
FileStream imageFile = File.OpenRead(filePath);
return imageFile;
}
catch (IOException ex)
{
Console.WriteLine(String.Format("An exception was thrown while trying to open file {0}", filePath));
Console.WriteLine("Exception is: ");
Console.WriteLine(ex.ToString());
throw;
}
}
}
}
El servicio principal.
using System;
using System.ServiceModel;
namespace Service
{
class Program
{
static void Main(string[] args)
{
try
{
using (var serviceHost = new ServiceHost(typeof(StreamService)))
{
serviceHost.Open();
Console.WriteLine("Press Any Key to end");
Console.ReadKey();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
El servicio app.config
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="StreamServiceBehavior">
<serviceMetadata httpGetEnabled="True" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="NewBinding0" transferMode="Streamed"/>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="StreamServiceBehavior" name="Service.StreamService">
<endpoint address="net.tcp://localhost:9000/streamserver" binding="netTcpBinding"
bindingConfiguration="NewBinding0" bindingName="" contract="Service.IStream" />
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/StreamService" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
Inicie el servicio, es posible que deba ejecutarse en la cuenta de administrador para abrir el socket. Cree una aplicación de consola de cliente y agregue una referencia de servicio utilizando la url http: // localhost: 8080 / StreamService, usando Servicio como el espacio de nombres para el cliente generado.
El cliente principal
using System;
using System.IO;
using Client.Service;
namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
using (StreamClient streamClient = new StreamClient())
{
streamClient.Open();
using (FileStream fileStream = new FileStream("c://temp//bigfile.exe",FileMode.Create))
{
streamClient.GetLargeObject().CopyTo(fileStream);
}
}
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
El archivo de configuración del cliente generado deberá modificarse ligeramente, aumente receiveTimeout y establezca maxReceivedMessageSize = "4294967295"
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IStream" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:30:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
maxReceivedMessageSize="4294967295">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:9000/streamserver" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IStream" contract="Service.IStream"
name="NetTcpBinding_IStream">
</endpoint>
</client>
</system.serviceModel>
Inicia el servicio y luego el cliente. Transmitirá un archivo grande sin problema.