Служба WCF TcpNetBinding StreamRequest не получает поток - PullRequest
4 голосов
/ 26 апреля 2011

Мой первый вопрос, так что будьте нежнее =)

Ниже приведены все .Net 4, VB.Net, в VS2010. Конечной целью является получение (полного) потока от клиента по привязке Tcp к службе IIS Hosted WCF. Проблема, с которой я сталкиваюсь, заключается в том, что служба не может прочитать ни одного байта из предоставленного потока. Теперь с мельчайшей мелочью ... Я убрал изрядное количество для краткости, но, дайте мне знать, если я пропустил что-то важное.

Договор на обслуживание следующий:

<ServiceContract(Namespace:="ImageSystem")> _
Public Interface IUploadService
    <OperationContract()> _
    Function UploadFile(ByVal file As ImageUpload) As ImageUpload
End Interface

Контракт данных Загрузка изображения выглядит следующим образом:

<MessageContract()> _
Public Class ImageUpload

#Region " Message Header "

    Private _ImageID As Nullable(Of Long)
    <MessageHeader()> _
    Public Property ImageID() As Nullable(Of Long)
        Get
            Return _ImageID
        End Get
        Set(ByVal value As Nullable(Of Long))
            _ImageID = value
        End Set
    End Property

    '... a few other value type properties

#End Region

#Region " Message Body"
    ' Do not add any more members to the message body or streaming support will be disabled!

    <MessageBodyMember()> _
    Public Data As System.IO.Stream

#End Region

End Class

Соответствующие конфигурации / привязки сервера следующие (это, очевидно, только настройки среды разработки):

<system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="netTcpStreamBinding" transferMode="Streamed" maxBufferSize="20971520" maxReceivedMessageSize="20971520"/>
      </netTcpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="UploadServiceBehaviour"
        name="ImageSystem.SVC.UploadService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpStreamBinding"
          contract="ImageSystem.SVC.IUploadService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:809/UploadService" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="UploadServiceBehaviour">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Служба WCF - это библиотека служб, которая размещается в веб-приложении. Проект веб-приложения выполняется в моем локальном IIS 7.5. IIS настроен на включение соединений TCP, а удостоверение пула приложений настроено с соответствующими разрешениями для реализации контракта. VS2010 запускается от имени администратора, чтобы включить отладку в IIS.

Для проверки реализации контракта у меня есть консольное приложение Windows, настроенное как (тестовый) клиент. Клиентские прокси-классы были созданы путем добавления ссылки на службу в службу на узле IIS (http://localhost/ImageSystem/UploadService.svc). Ссылка на службу настроена для генерации асинхронных методов.

Соответствующая автоматически сгенерированная конфигурация клиента выглядит следующим образом (обратите внимание, я пытался увеличить maxBufferPoolSize, maxBufferSize и maxReceivedMessageSize, чтобы соответствовать конфигурации серверов «20971520», но безрезультатно):

[ПРАВИТЬ: секция надежных сессий закомментирована в свете предложения Сиксто Саеза, но безрезультатно]

<system.serviceModel>

    <bindings>
        <binding name="NetTcpBinding_IUploadService" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
          hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="20971520"
          maxBufferSize="20971520" maxConnections="10" maxReceivedMessageSize="20971520">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <!--<reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />-->
          <security mode="None">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
            <message clientCredentialType="Windows" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

    <client>
      <endpoint address="net.tcp://mycomputername.mydomain/ImageSystem/UploadService.svc"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUploadService"
        contract="UploadService.Local.IUploadService" name="NetTcpBinding_IUploadService">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
    </client>

  </system.serviceModel>

Использование клиента выглядит следующим образом:

Public Sub Test()
    Dim serviceClient As UploadService.Local.UploadServiceClient = New UploadService.Local.UploadServiceClient
    AddHandler serviceClient.UploadFileCompleted, AddressOf LocalTestCallback
    Dim ms As MemoryStream = New MemoryStream
    My.Resources.Penguins.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
    serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "MYDOMAIN"
    serviceClient.ClientCredentials.Windows.ClientCredential.UserName = "User"
    serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "Password123"
    serviceClient.UploadFileAsync(Nothing, ..., ms, ms) '"..." is obviously not actually here, other values omitted. "ms" is passed as UserState object in addition to fulfilling the 'Data' parameter
End Sub

В случае, если вам интересно (или это имеет значение), изображение пингвинов - это изображение, которое поставляется с Windows 7 в каталоге примеров изображений. Размер изображения составляет 777 835 байт (должен быть в пределах максимального размера соответствующего запроса / буфера).

Я попробовал два подхода для чтения изображения на стороне сервера.

Подход 1:

Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile

    Dim uploadBuffer(Helper.Settings.AppSettings(Of Integer)("UploadBufferSize", True) - 1) As Byte
    Dim ms As MemoryStream = New MemoryStream()
    Dim bytesRead As Integer
    Do
        bytesRead = file.Data.Read(uploadBuffer, 0, uploadBuffer.Length)
        ms.Write(uploadBuffer, 0, bytesRead)
    Loop Until bytesRead = 0

End Function

Подход 2:

Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile

    Dim reader As StreamReader = New StreamReader(file.Data)
    Dim imageB64 As String = reader.ReadToEnd
    ms = New MemoryStream(Convert.FromBase64String(imageB64))

End Function

В обоих случаях ms.Length = 0. Более четко, во втором подходе imageB64 = "" (пустая строка).

Почему я ничего не получаю из потока? Кроме того, как подлый вопрос , почему сгенерированный прокси-класс не обеспечивает перегрузку, которая принимает объект типа ImageUpload ?

Заранее спасибо !!

Ответы [ 2 ]

2 голосов
/ 04 мая 2011

мне показалось странным, что у вас возникли проблемы, о которых вы упоминали, поэтому мне было любопытно, и я собрал реализацию, используя ваш контракт на обслуживание.Тот на самом деле работал сразу.На самом деле я не знаю, что пошло не так в вашем случае (это не очевидно), но позвольте мне опубликовать здесь рабочее решение, надеясь, что это поможет вам решить вашу проблему.

К сожалению, так как я отказался от VBмного лет назад я могу предоставить только код C #.Надеюсь, что все в порядке.

Сервер Web.config (протестирован в IIS, с привязкой net.tcp):

  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding transferMode="Streamed" maxReceivedMessageSize="1000000">
          <security mode="None"/>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="ImageSystem.SVC.UploadService">
        <endpoint address="" binding="netTcpBinding" contract="ImageSystem.SVC.IUploadService">
        </endpoint>
        <endpoint address="mex" kind="mexEndpoint" binding="mexTcpBinding"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Client app.config (consoleтестовое приложение):

<system.serviceModel>
    <bindings>
        <netTcpBinding>
            <binding transferMode="Streamed" maxReceivedMessageSize="1000000">
                <security mode="None"/>
            </binding>
        </netTcpBinding>
    </bindings>
    <client>
        <endpoint 
            address="net.tcp://localhost/WcfService1/UploadService.svc" 
            binding="netTcpBinding" 
            contract="ImageServices.IUploadService" 
            name="NetTcpBinding_IUploadService">
        </endpoint>
    </client>
</system.serviceModel>

Договор на обслуживание и выполнение:

[ServiceContract(Namespace="urn:ImageSystem")]
public interface IUploadService
{
    [OperationContract]
    ImageUpload UploadFile(ImageUpload file);
}

[MessageContract]
public class ImageUpload
{
    [MessageHeader]
    public long? ImageID { get; set; }
    [MessageBodyMember]
    public Stream Data;
}

public class UploadService : IUploadService
{

    public ImageUpload UploadFile(ImageUpload file)
    {
        long length;

        using (var ms = new MemoryStream())
        {
            file.Data.CopyTo(ms);
            length = ms.Length;
        }

        return new ImageUpload { ImageID = length, Data = new MemoryStream() };
    }
}

тестовое приложение:

private static readonly string imgPath = @"C:\Pictures\somepicture.jpg";
private static readonly EventWaitHandle waitHandle = new AutoResetEvent(false);

static void Main()
{
    long? result;

    using (var service = new ImageServices.UploadServiceClient("NetTcpBinding_IUploadService"))
    {
        var image = new ImageServices.ImageUpload();
        using (var imgStream = File.OpenRead(imgPath))
        {
            image.Data = imgStream;
            service.UploadFileCompleted += (sender, e) => 
            { 
                result = e.Result;
                if (e.Data != null) image.Data.Dispose();
                waitHandle.Set();
            };

            service.UploadFileAsync(null, imgStream);

            waitHandle.WaitOne();
        }
    }
}

First ofвсе, как вы можете видеть, файлы конфигурации могут быть намного проще.Особенно большое значение BufferSize не требуется.Тогда, что касается контракта на обслуживание, мне не понятно, почему операция Upload получит И вернет сообщение ImageUpload.В моей реализации я возвращаю размер загруженного файла в параметре ImageID, конечно, только для демонстрации.Я не знаю, каково было ваше обоснование этого контракта и что вы на самом деле хотели бы вернуть.


На самом деле, я собирался нажать кнопку «Отправить», когда у меня появилась идея, почему ваш код можетпровалился.В своем тестовом клиенте, прежде чем звонить serviceClient.UploadFileAsync(), добавьте эту строку в свой код: ms.Seek(0, SeekOrigin.Begin).

Сбрасывает позицию MemoryStream обратно в его начало.Если вы этого не сделаете, MemoryStream будет использоваться только с его текущей позиции, которая является его концом и которая объясняет длину = 0 потока, полученного на стороне обслуживания!

0 голосов
/ 26 апреля 2011

Возможно, вы уже видели информацию в этой статье MSDN , но вам следует ознакомиться с разделом «Потоковые данные» и перечисленными в нем ограничениями.Ваша клиентская конфигурация показывает элемент надежный сеанс с атрибутом order, установленным в значение «true», которое не поддерживается для потоковой передачи.

Я не знаю, является ли это именно причиной вашей проблемы, но это начало.В этой статье также довольно хорошо представлены основные конфигурации, необходимые для потоковой передачи, поэтому вы должны убедиться, что ваша конфигурация соответствует ее рекомендациям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...