C#で「TCPクライアント」を実装する

2020年3月11日

C#でTCPクライアントをササッと作る必要があったので、その時の個人用メモ。
ちなみに、C#のTCPサーバーの実装はこちら。

あとは、TCPクライアントとして、ネットにあるサンプルの「一回送受信して終わり」ではなく、常時稼働でずっと使えそうなソースサンプルを意識して書いてみた。けどまぁ時間があればもう少し手直しはします。
バグってたらコメントお願いします。

改訂履歴

(2022/10/03 修正)tcpの受信ループにて、1回目に受けきれなかった場合の2回目以降の受信も受信バッファの先頭にデータを格納していた問題を修正しました。ご指摘ありがとうございます。

(2021/03/27 修正)2点修正しました。ご指摘ありがとうございます。

  1. Read()関数について、TCP通信ではサーバーから全データ一括で受信できると限らず、分割される場合もあるため、読み残しが無いか確認する様に修正
  2. Read()関数について、例外を使用者側に再スローする様に修正

開発環境

  • VisualStudio2017
  • .Net4.7(Windows7と10が対応しているので)

基本方針

  • TCPクライアントをクラス化して、1サーバーごとに1インスタンスで管理できるようにする。

キーワード

  • TcpClient
  • NetworkStream
  • Read
  • IOException

【C#】ソースコード

TCPクライアント(TCPClient.cs)

TCPクライアントクラス。このクラスをインスタンス化して、アプリで制御する。
ソース全文をまるごと載せておきます。

namespaceとかは適当に変えて下さい。

エラー処理も結構適当です。使用される側でもう少し補強しておいてください。(接続先情報の範囲チェックとか)

ポイントは、接続状態「m_connected」をpublicにして、メインアプリ側から参照できるようにする。
メイン側は、未接続(false)であればConnect()を呼び、接続(true)であれば、必要に応じて送受信(Send() / Receive())を呼ぶようにする。

メイン側のサンプルソース

上記TCPクライアントクラスを使用したサンプルソースを載せておきます。

サンプルアプリとしては、以下の動きにしています。
・接続できていなければ接続
・接続できていれば5秒周期で5byteのデータ送信、完了すれば受信に進む
スレッドに組み込むなりなんなり、使えるかと思います。

実行例

実行したらこんな感じにログが出ます。

[2020/03/11 17:07:12]【TCPClient】Connect() : ERROR !!! 対象のコンピューターにて拒否されたため、接続できませんでした。 127.0.0.1:1234

[2020/03/11 17:07:17]【TCPClient】Connect() : [127.0.0.1:1234] に接続します .

[2020/03/11 17:07:17]【TCPClient】Connect() : 接続しました

[2020/03/11 17:07:17]【TCPClient】Send() : [0102030405]

[2020/03/11 17:07:18]【TCPClient】Receive() : 受信タイムアウト

[2020/03/11 17:07:25]【TCPClient】Receive() : 受信タイムアウト

[2020/03/11 17:07:30]【TCPClient】Receive() : ERROR !!! サーバーから切断されました

[2020/03/11 17:07:35]【TCPClient】Connect() : [127.0.0.1:1234] に接続します

[2020/03/11 17:07:36]【TCPClient】Connect() : ERROR !!! 対象のコンピューターによって拒否されたため、接続できませんでした。 127.0.0.1:1234

[2020/03/11 17:07:41]【TCPClient】Connect() : [127.0.0.1:1234] に接続します .

[2020/03/11 17:07:41]【TCPClient】Connect() : 接続しました

[2020/03/11 17:07:42]【TCPClient】Receive() : 受信タイムアウト

[2020/03/11 17:07:47]【TCPClient】Receive() : [000C00000000030111170746]

[2020/03/11 17:07:52]【TCPClient】Send() : [0102030405]

[2020/03/11 17:07:53]【TCPClient】Receive() : 受信タイムアウト

[2020/03/11 17:07:59]【TCPClient】Receive() : 受信タイムアウト

[2020/03/11 17:08:04]【TCPClient】Receive() : [000C00000000030111170802]

[2020/03/11 17:08:09]【TCPClient】Send() : [0102030405]

[2020/03/11 17:08:09]【TCPClient】Receive() : [000C00000000030111170807]

[2020/03/11 17:08:14]【TCPClient】Send() : [0102030405]

[2020/03/11 17:08:14]【TCPClient】Receive() : [000C00000000030111170812]

応用例

本ソースを用いて、三菱PLCと通信をおこなうテスターを作ってみましたので、参考にどうぞ。

まとめ

そんなに難しくないね。

間違いなどあれば、受け付けます。