diff --git a/docs/docs/advanced/raw-grpc.md b/docs/docs/advanced/raw-grpc.md index 4cd269d55..1eeb0d14b 100644 --- a/docs/docs/advanced/raw-grpc.md +++ b/docs/docs/advanced/raw-grpc.md @@ -1,5 +1,242 @@ # Raw gRPC APIs -MagicOnion can define and use primitive gRPC APIs(ClientStreaming, ServerStreaming, DuplexStreaming). Especially DuplexStreaming is used underlying StreamingHub. If there is no reason, we recommend using StreamingHub. + +MagicOnion can define and use primitive gRPC APIs (ClientStreaming, ServerStreaming, DuplexStreaming). Especially DuplexStreaming is used underlying StreamingHub. If there is no reason, we recommend using StreamingHub. + +## ServerStreaming + +ServerStreaming is a streaming pattern where the server sends multiple values to the client. The client sends a single request, and the server can return multiple responses. + +### Server-side implementation + +To implement ServerStreaming, use `GetServerStreamingContext()` to get the streaming context. + +```csharp +public async Task> GetWeatherUpdatesAsync(string location, int count) +{ + var stream = GetServerStreamingContext(); + + // Send weather data for the specified count + for (int i = 0; i < count; i++) + { + var weatherData = new WeatherData + { + Temperature = Random.Shared.Next(-10, 35), + Humidity = Random.Shared.Next(30, 90), + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(weatherData); + + // Wait for 1 second (simulating real-time data) + await Task.Delay(1000); + } + + return stream.Result(); +} +``` + +### Client-side implementation + +On the client side, use `ResponseStream.ReadAllAsync()` to receive all values sent from the server. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.GetWeatherUpdatesAsync("Tokyo", 5); + +await foreach (var weatherData in stream.ResponseStream.ReadAllAsync()) +{ + Console.WriteLine($"Temperature: {weatherData.Temperature}°C, Humidity: {weatherData.Humidity}%, Time: {weatherData.Timestamp}"); +} +``` + +### Use cases + +ServerStreaming is useful in scenarios such as: + +- Real-time data feeds (stock prices, sensor data, etc.) +- Sending large amounts of data in chunks +- Progress update notifications +- Log streaming + +## ClientStreaming + +ClientStreaming is a streaming pattern where the client sends multiple values to the server. The client sends multiple messages, and the server returns a single response. + +### Server-side implementation + +To implement ClientStreaming, use `GetClientStreamingContext()` to get the streaming context. + +```csharp +public async Task> AnalyzeSensorDataAsync() +{ + var stream = GetClientStreamingContext(); + + var allData = new List(); + + // Receive all data from the client + await foreach (var data in stream.ReadAllAsync()) + { + Logger.Debug($"Received sensor data: {data.Value} at {data.Timestamp}"); + allData.Add(data); + } + + // Analyze the received data + var result = new AnalysisResult + { + Average = allData.Average(d => d.Value), + Max = allData.Max(d => d.Value), + Min = allData.Min(d => d.Value), + Count = allData.Count + }; + + return stream.Result(result); +} +``` + +### Client-side implementation + +On the client side, use `RequestStream.WriteAsync()` to send multiple values and call `CompleteAsync()` at the end to complete the stream. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.AnalyzeSensorDataAsync(); + +// Send sensor data +for (int i = 0; i < 10; i++) +{ + var sensorData = new SensorData + { + Value = Random.Shared.NextDouble() * 100, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(sensorData); + await Task.Delay(100); // Simulate sensor reading interval +} + +// Complete the stream +await stream.RequestStream.CompleteAsync(); + +// Receive analysis result from server +var result = await stream.ResponseAsync; +Console.WriteLine($"Average: {result.Average}, Max: {result.Max}, Min: {result.Min}, Count: {result.Count}"); +``` + +### Use cases + +ClientStreaming is useful in scenarios such as: + +- File uploads (in chunks) +- Batch data submission +- Sensor data collection +- Bulk log submission + +## DuplexStreaming + +DuplexStreaming is a bidirectional streaming pattern where both client and server can send and receive multiple messages simultaneously. This is the underlying technology for MagicOnion's StreamingHub. + +### Server-side implementation + +To implement DuplexStreaming, use `GetDuplexStreamingContext()` to get the streaming context. + +```csharp +public async Task> ChatAsync() +{ + var stream = GetDuplexStreamingContext(); + + // Task to receive messages from the client + var receiveTask = Task.Run(async () => + { + await foreach (var message in stream.ReadAllAsync()) + { + Logger.Debug($"Received: {message.User}: {message.Content}"); + + // Echo back (return received message with server response) + var response = new ChatMessage + { + User = "Server", + Content = $"Echo: {message.Content}", + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(response); + } + }); + + // Send welcome message + await stream.WriteAsync(new ChatMessage + { + User = "Server", + Content = "Welcome to the chat!", + Timestamp = DateTime.UtcNow + }); + + await receiveTask; + + return stream.Result(); +} +``` + +### Client-side implementation + +On the client side, handle sending and receiving in parallel. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.ChatAsync(); + +// Task to receive messages from the server +var receiveTask = Task.Run(async () => +{ + await foreach (var message in stream.ResponseStream.ReadAllAsync()) + { + Console.WriteLine($"[{message.Timestamp}] {message.User}: {message.Content}"); + } +}); + +// Send user input +while (true) +{ + var input = Console.ReadLine(); + if (input == "exit") break; + + var message = new ChatMessage + { + User = "Client", + Content = input, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(message); +} + +// Complete the stream +await stream.RequestStream.CompleteAsync(); +await receiveTask; +``` + +### Use cases + +DuplexStreaming is useful in scenarios such as: + +- Real-time chat +- Bidirectional game communication +- Collaboration tools +- Real-time monitoring systems + +### Notes + +1. **Consider StreamingHub**: When you need DuplexStreaming, StreamingHub is often more suitable in many cases. StreamingHub provides a higher-level API built on top of DuplexStreaming. + +2. **Error handling**: Exceptions during streaming need to be handled properly. Implement measures for connection drops and timeouts. + +3. **Resource management**: Long-running streaming connections should manage resources properly and set timeouts as needed. + +4. **Concurrent processing**: In DuplexStreaming, sending and receiving happen concurrently, so pay attention to thread safety. + +## Sample Code + +### Server Sample ```csharp // Definitions @@ -83,7 +320,7 @@ public class MyFirstService : ServiceBase, IMyFirstService } ``` -Client sample. +### Client sample ```csharp static async Task ClientStreamRun(IMyFirstService client) @@ -127,4 +364,4 @@ static async Task DuplexStreamRun(IMyFirstService client) } } } -``` +``` \ No newline at end of file diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md b/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md new file mode 100644 index 000000000..e5e5efd14 --- /dev/null +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md @@ -0,0 +1,367 @@ +# Raw gRPC API + +MagicOnion はプリミティブな gRPC API(ClientStreaming、ServerStreaming、DuplexStreaming)を定義して使用することができます。特に DuplexStreaming は StreamingHub の基礎となって使用されています。特別な理由がない限り、StreamingHub の使用を推奨します。 + +## ServerStreaming の使い方 + +ServerStreaming はサーバーからクライアントへ複数の値を送信するストリーミングパターンです。クライアントは単一のリクエストを送信し、サーバーは複数のレスポンスを返すことができます。 + +### サーバー側の実装 + +ServerStreaming を実装するには、`GetServerStreamingContext()` を使用してストリーミングコンテキストを取得します。 + +```csharp +public async Task> GetWeatherUpdatesAsync(string location, int count) +{ + var stream = GetServerStreamingContext(); + + // 指定された回数だけ天気データを送信 + for (int i = 0; i < count; i++) + { + var weatherData = new WeatherData + { + Temperature = Random.Shared.Next(-10, 35), + Humidity = Random.Shared.Next(30, 90), + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(weatherData); + + // 1秒待機(リアルタイムデータのシミュレーション) + await Task.Delay(1000); + } + + return stream.Result(); +} +``` + +### クライアント側の実装 + +クライアント側では、`ResponseStream.ReadAllAsync()` を使用してサーバーから送信されるすべての値を受信します。 + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.GetWeatherUpdatesAsync("Tokyo", 5); + +await foreach (var weatherData in stream.ResponseStream.ReadAllAsync()) +{ + Console.WriteLine($"気温: {weatherData.Temperature}°C, 湿度: {weatherData.Humidity}%, 時刻: {weatherData.Timestamp}"); +} +``` + +### 使用例 + +ServerStreaming は以下のようなシナリオで有用です: + +- リアルタイムデータフィード(株価、センサーデータなど) +- 大量データの分割送信 +- プログレス更新の通知 +- ログのストリーミング + +## ClientStreaming の使い方 + +ClientStreaming はクライアントからサーバーへ複数の値を送信するストリーミングパターンです。クライアントは複数のメッセージを送信し、サーバーは単一のレスポンスを返します。 + +### サーバー側の実装 + +ClientStreaming を実装するには、`GetClientStreamingContext()` を使用してストリーミングコンテキストを取得します。 + +```csharp +public async Task> AnalyzeSensorDataAsync() +{ + var stream = GetClientStreamingContext(); + + var allData = new List(); + + // クライアントからのすべてのデータを受信 + await foreach (var data in stream.ReadAllAsync()) + { + Logger.Debug($"Received sensor data: {data.Value} at {data.Timestamp}"); + allData.Add(data); + } + + // 受信したデータを分析 + var result = new AnalysisResult + { + Average = allData.Average(d => d.Value), + Max = allData.Max(d => d.Value), + Min = allData.Min(d => d.Value), + Count = allData.Count + }; + + return stream.Result(result); +} +``` + +### クライアント側の実装 + +クライアント側では、`RequestStream.WriteAsync()` を使用して複数の値を送信し、最後に `CompleteAsync()` を呼び出してストリームを完了します。 + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.AnalyzeSensorDataAsync(); + +// センサーデータを送信 +for (int i = 0; i < 10; i++) +{ + var sensorData = new SensorData + { + Value = Random.Shared.NextDouble() * 100, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(sensorData); + await Task.Delay(100); // センサー読み取り間隔のシミュレーション +} + +// ストリームを完了 +await stream.RequestStream.CompleteAsync(); + +// サーバーからの分析結果を受信 +var result = await stream.ResponseAsync; +Console.WriteLine($"平均: {result.Average}, 最大: {result.Max}, 最小: {result.Min}, 件数: {result.Count}"); +``` + +### 使用例 + +ClientStreaming は以下のようなシナリオで有用です: + +- ファイルアップロード(チャンク単位) +- バッチデータの送信 +- センサーデータの収集 +- ログの一括送信 + +## DuplexStreaming の使い方 + +DuplexStreaming は双方向のストリーミングパターンで、クライアントとサーバーが同時に複数のメッセージを送受信できます。これは MagicOnion の StreamingHub の基礎となる技術です。 + +### サーバー側の実装 + +DuplexStreaming を実装するには、`GetDuplexStreamingContext()` を使用してストリーミングコンテキストを取得します。 + +```csharp +public async Task> ChatAsync() +{ + var stream = GetDuplexStreamingContext(); + + // クライアントからのメッセージを受信するタスク + var receiveTask = Task.Run(async () => + { + await foreach (var message in stream.ReadAllAsync()) + { + Logger.Debug($"Received: {message.User}: {message.Content}"); + + // エコーバック(受信したメッセージにサーバー応答を付けて返す) + var response = new ChatMessage + { + User = "Server", + Content = $"Echo: {message.Content}", + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(response); + } + }); + + // ウェルカムメッセージを送信 + await stream.WriteAsync(new ChatMessage + { + User = "Server", + Content = "チャットへようこそ!", + Timestamp = DateTime.UtcNow + }); + + await receiveTask; + + return stream.Result(); +} +``` + +### クライアント側の実装 + +クライアント側では、送信と受信を並行して処理します。 + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.ChatAsync(); + +// サーバーからのメッセージを受信するタスク +var receiveTask = Task.Run(async () => +{ + await foreach (var message in stream.ResponseStream.ReadAllAsync()) + { + Console.WriteLine($"[{message.Timestamp}] {message.User}: {message.Content}"); + } +}); + +// ユーザー入力を送信 +while (true) +{ + var input = Console.ReadLine(); + if (input == "exit") break; + + var message = new ChatMessage + { + User = "Client", + Content = input, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(message); +} + +// ストリームを完了 +await stream.RequestStream.CompleteAsync(); +await receiveTask; +``` + +### 使用例 + +DuplexStreaming は以下のようなシナリオで有用です: + +- リアルタイムチャット +- ゲームの双方向通信 +- コラボレーションツール +- リアルタイム監視システム + +### 注意事項 + +1. **StreamingHub の検討**: DuplexStreaming が必要な場合、多くのケースで StreamingHub の方が適しています。StreamingHub は DuplexStreaming の上に構築された、より高レベルな API を提供します。 + +2. **エラーハンドリング**: ストリーミング中の例外は適切に処理する必要があります。接続の切断やタイムアウトに対する対策を実装してください。 + +3. **リソース管理**: 長時間実行されるストリーミング接続は、適切にリソースを管理し、必要に応じてタイムアウトを設定してください。 + +4. **並行処理**: DuplexStreaming では送信と受信が並行して行われるため、スレッドセーフティに注意してください。 + +## サンプルコード + +### サーバーのサンプル + +```csharp +// 定義 +public interface IMyFirstService : IService +{ + UnaryResult SumAsync(int x, int y); + Task> SumLegacyTaskAsync(int x, int y); + Task> ClientStreamingSampleAsync(); + Task> ServerStreamingSampleAsync(int x, int y, int z); + Task> DuplexStreamingSampleAsync(); +} + +// サーバー +public class MyFirstService : ServiceBase, IMyFirstService +{ + public async UnaryResult SumAsync(int x, int y) + { + Logger.Debug($"Called SumAsync - x:{x} y:{y}"); + + return (x + y).ToString(); + } + + public async Task> ClientStreamingSampleAsync() + { + Logger.Debug($"Called ClientStreamingSampleAsync"); + + // ClientStreaming の場合は、GetClientStreamingContext を使用します。 + var stream = GetClientStreamingContext(); + + // クライアントから非同期で受信 + await foreach (var x in stream.ReadAllAsync()) + { + Logger.Debug("Client Stream Received:" + x); + } + + // StreamingContext.Result() で結果値を返します。 + return stream.Result("finished"); + } + + public async Task> ServerStreamingSampleAsync(int x, int y, int z) + { + Logger.Debug($"Called ServerStreamingSampleAsync - x:{x} y:{y} z:{z}"); + + var stream = GetServerStreamingContext(); + + var acc = 0; + for (int i = 0; i < z; i++) + { + acc = acc + x + y; + await stream.WriteAsync(acc.ToString()); + } + + return stream.Result(); + } + + public async Task> DuplexStreamingSampleAsync() + { + Logger.Debug($"Called DuplexStreamingSampleAsync"); + + // DuplexStreamingContext はサーバーとクライアントの両方のストリーミングを表します。 + var stream = GetDuplexStreamingContext(); + + var waitTask = Task.Run(async () => + { + // ForEachAsync(MoveNext, Current) でクライアントストリーミングを受信できます。 + await foreach (var x in stream.ReadAllAsync()) + { + Logger.Debug($"Duplex Streaming Received:" + x); + } + }); + + // WriteAsync は ServerStreaming です。 + await stream.WriteAsync("test1"); + await stream.WriteAsync("test2"); + await stream.WriteAsync("finish"); + + await waitTask; + + return stream.Result(); + } +} +``` + +### クライアントのサンプル + +```csharp +static async Task ClientStreamRun(IMyFirstService client) +{ + var stream = await client.ClientStreamingSampleAsync(); + + for (int i = 0; i < 3; i++) + { + await stream.RequestStream.WriteAsync(i); + } + await stream.RequestStream.CompleteAsync(); + + var response = await stream.ResponseAsync; + + Console.WriteLine("Response:" + response); +} + +static async Task ServerStreamRun(IMyFirstService client) +{ + var stream = await client.ServerStreamingSampleAsync(10, 20, 3); + + await foreach (var x in stream.ResponseStream.ReadAllAsync()) + { + Console.WriteLine("ServerStream Response:" + x); + } +} + +static async Task DuplexStreamRun(IMyFirstService client) +{ + var stream = await client.DuplexStreamingSampleAsync(); + + var count = 0; + await foreach (var x in stream.ResponseStream.ReadAllAsync()) + { + Console.WriteLine("DuplexStream Response:" + x); + + await stream.RequestStream.WriteAsync(count++); + if (x == "finish") + { + await stream.RequestStream.CompleteAsync(); + } + } +} +``` diff --git a/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md b/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md index b762e084b..ecbce616a 100644 --- a/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md +++ b/docs/i18n/ko/docusaurus-plugin-content-docs/current/advanced/raw-grpc.md @@ -1,6 +1,243 @@ # Raw gRPC APIs + MagicOnion은 기본적인 gRPC APIs(ClientStreaming, ServerStreaming, DuplexStreaming)를 정의하고 사용할 수 있습니다. 특히 DuplexStreaming은 StreamingHub의 기반으로 사용됩니다. 특별한 이유가 없다면 StreamingHub를 사용하는 것을 권장합니다. +## ServerStreaming + +ServerStreaming은 서버에서 클라이언트로 여러 값을 전송하는 스트리밍 패턴입니다. 클라이언트는 단일 요청을 보내고, 서버는 여러 응답을 반환할 수 있습니다. + +### 서버 측 구현 + +ServerStreaming을 구현하려면 `GetServerStreamingContext()`를 사용하여 스트리밍 컨텍스트를 가져옵니다. + +```csharp +public async Task> GetWeatherUpdatesAsync(string location, int count) +{ + var stream = GetServerStreamingContext(); + + // 지정된 횟수만큼 날씨 데이터 전송 + for (int i = 0; i < count; i++) + { + var weatherData = new WeatherData + { + Temperature = Random.Shared.Next(-10, 35), + Humidity = Random.Shared.Next(30, 90), + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(weatherData); + + // 1초 대기 (실시간 데이터 시뮬레이션) + await Task.Delay(1000); + } + + return stream.Result(); +} +``` + +### 클라이언트 측 구현 + +클라이언트 측에서는 `ResponseStream.ReadAllAsync()`를 사용하여 서버에서 전송되는 모든 값을 수신합니다. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.GetWeatherUpdatesAsync("Tokyo", 5); + +await foreach (var weatherData in stream.ResponseStream.ReadAllAsync()) +{ + Console.WriteLine($"온도: {weatherData.Temperature}°C, 습도: {weatherData.Humidity}%, 시간: {weatherData.Timestamp}"); +} +``` + +### 사용 사례 + +ServerStreaming은 다음과 같은 시나리오에서 유용합니다: + +- 실시간 데이터 피드 (주식 가격, 센서 데이터 등) +- 대량 데이터의 청크 단위 전송 +- 진행 상황 업데이트 알림 +- 로그 스트리밍 + +## ClientStreaming + +ClientStreaming은 클라이언트에서 서버로 여러 값을 전송하는 스트리밍 패턴입니다. 클라이언트는 여러 메시지를 보내고, 서버는 단일 응답을 반환합니다. + +### 서버 측 구현 + +ClientStreaming을 구현하려면 `GetClientStreamingContext()`를 사용하여 스트리밍 컨텍스트를 가져옵니다. + +```csharp +public async Task> AnalyzeSensorDataAsync() +{ + var stream = GetClientStreamingContext(); + + var allData = new List(); + + // 클라이언트로부터 모든 데이터 수신 + await foreach (var data in stream.ReadAllAsync()) + { + Logger.Debug($"센서 데이터 수신: {data.Value} at {data.Timestamp}"); + allData.Add(data); + } + + // 수신된 데이터 분석 + var result = new AnalysisResult + { + Average = allData.Average(d => d.Value), + Max = allData.Max(d => d.Value), + Min = allData.Min(d => d.Value), + Count = allData.Count + }; + + return stream.Result(result); +} +``` + +### 클라이언트 측 구현 + +클라이언트 측에서는 `RequestStream.WriteAsync()`를 사용하여 여러 값을 전송하고, 마지막에 `CompleteAsync()`를 호출하여 스트림을 완료합니다. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.AnalyzeSensorDataAsync(); + +// 센서 데이터 전송 +for (int i = 0; i < 10; i++) +{ + var sensorData = new SensorData + { + Value = Random.Shared.NextDouble() * 100, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(sensorData); + await Task.Delay(100); // 센서 읽기 간격 시뮬레이션 +} + +// 스트림 완료 +await stream.RequestStream.CompleteAsync(); + +// 서버로부터 분석 결과 수신 +var result = await stream.ResponseAsync; +Console.WriteLine($"평균: {result.Average}, 최대: {result.Max}, 최소: {result.Min}, 개수: {result.Count}"); +``` + +### 사용 사례 + +ClientStreaming은 다음과 같은 시나리오에서 유용합니다: + +- 파일 업로드 (청크 단위) +- 배치 데이터 제출 +- 센서 데이터 수집 +- 대량 로그 제출 + +## DuplexStreaming + +DuplexStreaming은 클라이언트와 서버가 동시에 여러 메시지를 주고받을 수 있는 양방향 스트리밍 패턴입니다. 이는 MagicOnion의 StreamingHub를 위한 기반 기술입니다. + +### 서버 측 구현 + +DuplexStreaming을 구현하려면 `GetDuplexStreamingContext()`를 사용하여 스트리밍 컨텍스트를 가져옵니다. + +```csharp +public async Task> ChatAsync() +{ + var stream = GetDuplexStreamingContext(); + + // 클라이언트로부터 메시지를 수신하는 태스크 + var receiveTask = Task.Run(async () => + { + await foreach (var message in stream.ReadAllAsync()) + { + Logger.Debug($"수신: {message.User}: {message.Content}"); + + // 에코백 (수신한 메시지에 서버 응답을 추가하여 반환) + var response = new ChatMessage + { + User = "Server", + Content = $"Echo: {message.Content}", + Timestamp = DateTime.UtcNow + }; + + await stream.WriteAsync(response); + } + }); + + // 환영 메시지 전송 + await stream.WriteAsync(new ChatMessage + { + User = "Server", + Content = "채팅에 오신 것을 환영합니다!", + Timestamp = DateTime.UtcNow + }); + + await receiveTask; + + return stream.Result(); +} +``` + +### 클라이언트 측 구현 + +클라이언트 측에서는 송신과 수신을 병렬로 처리합니다. + +```csharp +var client = MagicOnionClient.Create(channel); +var stream = await client.ChatAsync(); + +// 서버로부터 메시지를 수신하는 태스크 +var receiveTask = Task.Run(async () => +{ + await foreach (var message in stream.ResponseStream.ReadAllAsync()) + { + Console.WriteLine($"[{message.Timestamp}] {message.User}: {message.Content}"); + } +}); + +// 사용자 입력 전송 +while (true) +{ + var input = Console.ReadLine(); + if (input == "exit") break; + + var message = new ChatMessage + { + User = "Client", + Content = input, + Timestamp = DateTime.UtcNow + }; + + await stream.RequestStream.WriteAsync(message); +} + +// 스트림 완료 +await stream.RequestStream.CompleteAsync(); +await receiveTask; +``` + +### 사용 사례 + +DuplexStreaming은 다음과 같은 시나리오에서 유용합니다: + +- 실시간 채팅 +- 양방향 게임 통신 +- 협업 도구 +- 실시간 모니터링 시스템 + +### 주의사항 + +1. **StreamingHub 고려**: DuplexStreaming이 필요한 경우, 많은 경우에 StreamingHub가 더 적합합니다. StreamingHub는 DuplexStreaming 위에 구축된 더 높은 수준의 API를 제공합니다. + +2. **오류 처리**: 스트리밍 중 예외는 적절히 처리되어야 합니다. 연결 끊김 및 타임아웃에 대한 대책을 구현하세요. + +3. **리소스 관리**: 장시간 실행되는 스트리밍 연결은 리소스를 적절히 관리하고 필요에 따라 타임아웃을 설정해야 합니다. + +4. **동시 처리**: DuplexStreaming에서는 송신과 수신이 동시에 발생하므로 스레드 안전성에 주의하세요. + +## 샘플 코드 + +### 서버 샘플 + ```csharp // Definitions public interface IMyFirstService : IService @@ -83,7 +320,7 @@ public class MyFirstService : ServiceBase, IMyFirstService } ``` -클라이언트 샘플 +### 클라이언트 샘플 ```csharp static async Task ClientStreamRun(IMyFirstService client) @@ -127,4 +364,4 @@ static async Task DuplexStreamRun(IMyFirstService client) } } } -``` +``` \ No newline at end of file