質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

Q&A

1回答

681閲覧

TCPクライアントのプログラムを最適化したい

退会済みユーザー

退会済みユーザー

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

0グッド

2クリップ

投稿2018/05/02 00:18

編集2022/01/12 10:55

背景

現在TCPクライアントを作成しています。
以下のプログラム(MyTcpClient.cs)は問題なく動作するのですが、全体的に冗長な気がしています。

例えば、イベントハンドラの登録解除をメソッド内で行っていたり、Task.Delay(); で待機している部分等が気になります。

質問

UserLoginRequestメソッドの処理を最適化するアイデアやTCPネットワークプログラム用のデザインパターン等ありますでしょうか?

eventを使っている理由について

リクエストとレスポンスが1:1の関係でない為です。

HTTPであれば以下のように書くことが出来ます。
リクエストとレスポンスが1:1の関係です。

C#

1var client = new HttpClient(); 2var responseString = await client.GetStringAsync("http://example.com"); 3// TCPでもこんな感じに書きたい

しかし、System.Net.Socketsの場合、自分で実装しなければならず、リクエストとレスポンスが1:1で取れない場合を考慮する必要がありました。任意のリクエストをしてから期待するレスポンスを受け取るまで、サーバーから異なるデータを送信し、そのデータを受け取る可能性があるからです。

なので、レスポンスデータの1バイト目で何のデータかを区別して個別のeventを発火させることにしました。

試したこと

  • corefx のソースコードを読んだ

corefxのソースコードでHttpClientはTCPをどのように扱い、上位層のHTTPとして実装してるかが参考になるかと思いHttpClientのソースコードを読みましたが、WindowsであればWinHttp, unixであればcurlを呼び出しているだけで、詳細は分かりませんでした。
(WinHTTPはOSSでない為ソースコードが見れません。)
(curlのソースコードはC言語で書かれていました。Cに対する理解が深くないので読むのが辛くて読んでません。)

プログラム

  • Program.cs

c#

1 static async Task Main(string[] args) 2{ 3 var client = new MyTcpClient(); 4 var requestBytes = new byte[] { }; 5 var responseBytes = await client.UserLoginRequest(requestBytes); 6}
  • MyTcpClient.cs

c#

1using System; 2using System.Linq; 3using System.Net.Sockets; 4using System.Threading.Tasks; 5 6namespace TcpTest 7{ 8 class MyTcpClient 9 { 10 private Socket MyTcpClientSocket { get; set; } 11 12 private const int BufferSize = 1024; 13 public byte[] Buffer { get; } = new byte[BufferSize]; 14 15 private delegate void LoginDataReceivedEventHandler(object sender, byte[] bytes); 16 private event LoginDataReceivedEventHandler LoginDataReceived; 17 18 public MyTcpClient() 19 { 20 MyTcpClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 21 MyTcpClientSocket.Connect("127.0.0.1", 1000); 22 23 // 非同期で受信を待機 24 MyTcpClientSocket.BeginReceive(Buffer, 0, BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), MyTcpClientSocket); 25 } 26 27 private void ReceiveCallback(IAsyncResult asyncResult) 28 { 29 var socket = asyncResult.AsyncState as Socket; 30 31 var receiveSize = 0; 32 try 33 { 34 // 受信を待機 35 receiveSize = socket.EndReceive(asyncResult); 36 } 37 catch (Exception ex) 38 { 39 Console.WriteLine(ex.Message); 40 return; 41 } 42 43 if (receiveSize > 0) 44 { 45 var bytes = Buffer.Take(receiveSize).ToArray(); 46 47 if (bytes[0] == 1) 48 { 49 // 1バイト目が1の場合UserLoginRequetに対するレスポンスデータ 50 LoginDataReceived?.Invoke(this, bytes); 51 } 52 53 socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, ReceiveCallback, socket); 54 } 55 } 56 57 public async Task<byte[]> UserLoginRequest(byte[] requestBytes) 58 { 59 byte[] responseBytes = null; 60 61 var eventHandler = new LoginDataReceivedEventHandler(delegate (object _sender, byte[] _bytes) 62 { 63 responseBytes = _bytes; 64 }); 65 66 // イベントハンドラを登録 67 LoginDataReceived += eventHandler; 68 69 // サーバにリクエストを送信 70 MyTcpClientSocket.Send(requestBytes); 71 72 // レスポンスを待つ 73 while (responseBytes == null) 74 { 75 await Task.Delay(100); 76 } 77 78 // イベントハンドラを解除 79 LoginDataReceived -= eventHandler; 80 81 return responseBytes; 82 } 83 } 84}

環境

  • Windows 10
  • Visual Studio 2017
  • C# 7.2
  • .NET Standard 2.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

今回の問題とは直接関係ないことですが・・・(どうしても気になったので一言。余計なお世話だと思ったらスルーしてください)

Exception をキャッチして発生した例外をすべて握りつぶすのは止めた方がいいと思います。理由は以下の記事を見てください。

NETの例外処理 Part.1
https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/

.NETの例外処理 Part.2
https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/

.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

破損状態例外を処理する
https://msdn.microsoft.com/ja-jp/magazine/dd419661.aspx

投稿2018/05/02 05:56

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問