【C#】TCP/IPでチャットアプリを作成する方法を解説【後編】

C#

はじめに

前編ではサーバーアプリケーションの作成までを解説しました。
後編では、クライアントアプリケーションの作成~完成までを解説します。

クライアントアプリケーションはサーバーアプリケーションの処理と似ているため、前編の内容が理解できた方ならすぐに理解できると思います。

 

解説で使用しているソースコード

記事内にソースコードを全て掲載していますが、ソリューション毎ダウンロードしたいという方は以下Githubからダウンロード出来ます。

アプリケーションの作成

クライアントアプリケーションプロジェクトの作成

前半で作成したChatAppソリューションにClientAppプロジェクトを追加します。

ソリューションエクスプローラーを右クリック>追加>新しいプロジェクトを選択します。
Windowsフォームアプリケーション(.NET Framework)を選択し、プロジェクト名に「ClientApp」を設定します。

 

クライアントアプリケーションの作成

作成したClientAppプロジェクトのForm1を以下の様に変更します。

コントロール名プロパティ名説明
FormNameClientFormアプリケーションメインフォーム
 TextClientフォーム左上に表示される文字列を設定
gbSettingsText設定設定グループボックス
txtAddressText127.0.0.1デフォルトではループバックアドレスを設定。
(自分自身のPCのローカルIPアドレス)
txtDestPort  接続先ポート番号テキストボックス
txtSourcePortText0接続元ポート番号
(0を指定すると、OSが使用可能なポート番号を割り振ってくれる)
btnStartText接続開始ボタン
 ClickbtnStart_Clickクリックイベント
btnEndText切断終了ボタン
 ClickbtnEnd_Clickクリックイベント
 Enabledfalse 
gpOperationText操作 
txtNameText名無しさんチャットの送信者名デフォルト値
txtMessage  チャット内容テキストボックス
gpConnectionLogTextログロググループボックス
lbLog  ログリストボックス

ソースコード

ClientApp.csの全体のコードを記載します。
すべてコピペで動作すると思います。
ChatAppCommonへの参照が必要です。

ClientApp.cs

 

コードの解説

処理の要所を解説します。

ポート番号、IPアドレスのチェック

 

サーバーアプリケーションで実装したチェック処理に、IPアドレスのチェックを追加したものです。
System.Net.IPAddressクラスのTryParseでIPアドレスが変換できなかった場合は無効なIPアドレスと判定しています。

クライアントの作成~サーバーへの接続~データ受信待機

 

ポイント
  1. 接続情報(ポート番号、IPアドレス)の有効チェック。
  2. クライアントを作成してサーバーへ接続。
  3. 非同期でサーバーからのデータ受信を待機。
  1. 接続情報(ポート番号、IPアドレス)の有効チェック。
    ポート番号、IPアドレスの有効チャックを行い、有効な場合のみ後続の処理を実行します。

  2. クライアントを作成してサーバーへ接続。
    サーバーとやりとりをするためのクライアントを作成します。
    TcpClientのコンストラクタに接続元情報を渡すことでクライアントを作成できます。

    サーバーへの接続は、TcpClient.Connectメソッドを使用します。
    Connectメソッドの引数に接続先情報を渡して接続要求を投げます。

    接続に失敗した場合、ConnectメソッドでSystem.Net.Sockets.SocketException例外が発生するため、例外の有無で接続の成否を判定できます。
    Connectの戻り値で接続の成否を返してくれたほうが使い勝手がいいのになあ・・・とか思ったり思わなかったり。

  3. 非同期でサーバーからのデータ受信を待機。
    サーバーからの非同期データ受信待機は、サーバーアプリケーションで実装した非同期受信待機と同様の方法です。
    TcpClient.Client.BeginReceiveメソッドを使用します。

    繰り返しになってしまいますが、BeginReceiveメソッドの引数には
    ・受信するデータのバッファ(byte[])
    ・受信するデータのバッファ内の読込み開始位置(int)
    ・バッファサイズ(int)
    ・送受信時の動作(SocketFlags)
    ・受信時コールバックデリゲート(受信時に実行される処理)
    ・コールバックに渡されるユーザー定義オブジェクト(自由に設定出来るオブジェクト)
    を指定します。

    ユーザー定義オブジェクトには、ChatAppCommonに定義した通信データクラスを設定しています。

非同期データ受信処理

サーバーからの非同期データ受信待機で設定したコールバック処理です。
サーバーアプリケーションからデータを受信したときに実行されます。

ポイント
  1. BeginReceiveの引数に指定したユーザー定義型からデータを取得する。
  2. データ受信に失敗した場合、サーバーから切断されたと判定する。
  3. 受信データを画面に表示する。
  4. 受信処理後、再度受信待機する。
  1. BeginReceiveの引数に指定したユーザー定義型からデータを取得する。
    IAsyncResult.AsyncStateにはBeginReceiveメソッドの引数に渡したユーザー定義型のオブジェクトが設定されています。
    object型なので、渡したユーザー定義型にキャストして取得します。

  2. データ受信に失敗した場合、サーバーから切断されたと判定する。
    TcpClient.Client.EndReceiveメソッドでサーバーからのデータを取得しますが、サーバーから切断されている場合例外となります。(切断されたサーバーからデータを取得しようとするため)
    ここでは、切断されたことを画面に表示し、非同期データ受信を終了しています。

    再度サーバーアプリケーションとやりとりをするには、接続からやり直す必要があります。

  3. 受信データを画面に表示する。
    サーバーアプリケーションから受信したデータを画面に表示します。
    これは特に解説する事もないですね。

    しいて言うなら、非同期処理からUIスレッドへのアクセスになるため、Invokeを使用してUIスレッドで画面コントロールにデータを設定する必要がある という事でしょうか。

  4. 受信処理後、再度受信待機する。
    再度TcpClient.Client.BeginReceiveを実行し、データの受信を待機します。
    これで切断されるまでデータ受信待機→データ受信→クライアントに転送→再度データ受信待機→…というループになります。

アプリケーションの動作確認

サーバーアプリケーションを起動して、ポートの監視を開始します。

クライアンアプリケーションを複数起動し、接続先ポート番号にサーバーアプリケーションで監視しているポート番号を指定して接続します。

接続したらクライアン同士でメッセージを送信します。
サーバーの切断、クライアンの切断、メッセージの送信が正常に行われている事がわかります。

まとめ

以上でチャットアプリケーションの作成は完了になります。

非同期での通信は色々と考慮すべき点が多いです。
サーバーでデータ受信処理中にクライアントから切断されたり等、例外をすべてハンドリングするのも大変です。
今回は例外を握りつぶしちゃっていますが、もっと真面目に作るのであれば、切断された時に再接続を試みたり、一定周期でポーリング(クライアントに接続しているか確認するPingを送信)したりする必要があると思います。

需要があるようであれば、データ送信先クライアンの指定、ファイルの送信等の機能拡張の記事を書こうかと思いますので、気軽にコメントやDMで連絡をお願いします。

また、わかりにくいところがありましたら、気軽にコメントやDMで連絡をお願いします。
可能な限り対応させていただきます。

C#プログラミング
凡人プログラマーのブログ

コメント