Это корректно, который в WCF, я не могу сделать, чтобы сервис записал в поток, который получен клиентом?
Потоковая передача поддерживается в WCF для запросов, ответов или обоих. Я хотел бы поддерживать сценарий, где генератор данных (или клиент в случае переданного потоком запроса или сервер в случае переданного потоком ответа) может Записать на потоке. Это поддерживается?
Аналогией является Ответ. OutputStream от запроса ASP.NET. В ASPNET любая страница может вызвать Write на поток вывода, и содержание получено клиентом. Я могу сделать, что-то подобное в сервисе WCF - вызывает Write на поток, который получен клиентом?
Позвольте мне объяснить с иллюстрацией WCF. Самым простым примером Потоковой передачи в WCF является сервис, возвращая FileStream клиенту. Это - переданный потоком ответ. Серверный код для реализации этого, похож на это:
[ServiceContract]
public interface IStreamService
{
[OperationContract]
Stream GetData(string fileName);
}
public class StreamService : IStreamService
{
public Stream GetData(string filename)
{
return new FileStream(filename, FileMode.Open)
}
}
И клиентский код похож на это:
StreamDemo.StreamServiceClient client =
new WcfStreamDemoClient.StreamDemo.StreamServiceClient();
Stream str = client.GetData(@"c:\path\on\server\myfile.dat");
do {
b = str.ReadByte(); //read next byte from stream
...
} while (b != -1);
(пример, взятый из http://blog.joachim.at/?p=33)
Ясный, правильно? Сервер возвращает Поток клиенту, и клиент вызывает Read на него.
Для клиента действительно ли возможно обеспечить Поток и сервер для вызова Write на него?
Другими словами, а не модель приема - где клиент вытягивает данные из сервера - это - модель передачи, где клиент обеспечивает поток "приемника" и записи сервера в него. Серверный код мог бы выглядеть подобным этому:
[ServiceContract]
public interface IStreamWriterService
{
[OperationContract]
void SendData(Stream clientProvidedWritableStream);
}
public class DataService : IStreamWriterService
{
public void GetData(Stream s)
{
do {
byte[] chunk = GetNextChunk();
s.Write(chunk,0, chunk.Length);
} while (chunk.Length > 0);
}
}
Действительно ли это возможно в WCF, и если так, как? Что настройки конфигурации требуются для привязки, интерфейса, и т.д.? Какова терминология?
Возможно, это будет просто работать? (Я не попробовал его),
Спасибо.
Я совершенно уверен, что не существует комбинации привязок WCF, которая позволит вам буквально писать в поток клиента. Кажется логичным, что они должны быть, учитывая, что где-то под поверхностью определенно будет NetworkStream
, но как только вы начнете добавлять шифрование и все эти забавные вещи, WCF должен будет знать, как обернуть этот поток, чтобы превратить его в "настоящее" сообщение, чего, как мне кажется, он не делает.
Однако, сценарий I-need-to-return-a-stream-but-component-X-wants-to-write-to-one является общим, и у меня есть универсальное решение, которое я использую для этого сценария, который заключается в создании двустороннего потока и порождении рабочего потока для записи в него. Самый простой и безопасный способ сделать это (под этим я подразумеваю способ, который включает в себя написание наименьшего количества кода и, следовательно, наименьшую вероятность ошибок) - использовать анонимные трубы.
Вот пример метода, возвращающего AnonymousPipeClientStream
который можно использовать в WCF-сообщениях, результатах MVC и так далее - везде, где вы хотите изменить направление:
static Stream GetPipedStream(Action<Stream> writeAction)
{
AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream();
ThreadPool.QueueUserWorkItem(s =>
{
using (pipeServer)
{
writeAction(pipeServer);
pipeServer.WaitForPipeDrain();
}
});
return new AnonymousPipeClientStream(PipeDirection.In, pipeServer.ClientSafePipeHandle);
}
Пример использования этого метода:
static Stream GetTestStream()
{
string data = "The quick brown fox jumps over the lazy dog.";
return GetPipedStream(s =>
{
StreamWriter writer = new StreamWriter(s);
writer.AutoFlush = true;
for (int i = 0; i < 50; i++)
{
Thread.Sleep(50);
writer.WriteLine(data);
}
});
}
Только два предостережения относительно этого подхода:
Он потерпит неудачу, если writeAction
сделает что-нибудь для утилизации потока (вот почему метод тестирования не утилизирует StreamWriter
- потому что это привело бы к утилизации основного потока);
Он может потерпеть неудачу, если writeAction
фактически не записывает никаких данных, потому что WaitForPipeDrain
вернется немедленно и создаст состояние гонки. (Если это вызывает беспокойство, вы можете написать более защитный код, чтобы избежать этого, но это сильно усложняет ситуацию для редкого крайнего случая.)
Надеюсь, это поможет в вашем конкретном случае.
Потоковая передача WCF работает в обоих направлениях - ваши клиенты могут загружать данные в потоке, но ваша служба WCF также может отправлять данные в потоке. Вы определенно можете написать службу, которая выполняет потоковую передачу больших файлов на основе их имени файла или некоторого идентификатора или чего-то еще - абсолютно.
Если вы хотите отправить данные обратно из службы, вам нужно сделать это в возвращаемом значении вашего метода службы, которое должно иметь тип Stream
- вы не можете (насколько мне известно ) «предоставить» поток для записи - служба WCF создаст поток ответа и отправит его обратно.
Проверяли ли вы MSDN Streaming Message Transfer или сообщение в блоге Дэвида Вуда или сообщение в блоге Кьелл-Сверре о потоковой передаче WCF? Все они прекрасно показывают, какие настройки конфигурации вам нужны (в основном установка TransferMode
в конфигурации привязки на Streamed
, StreamedRequest
или StreamedResponse
).