特集

.NETネットワーク・プログラミング入門

高木 健一
http://www.woodensoldier.info/
2004/12/18
Page1 Page2 Page3 Page4

■クライアント側(ファイル送信側)における接続の確立

 クライアントからサーバへの接続を確立するコードは次のように記述する。

// クライアントのソケットを用意
TcpClient client = new TcpClient(this.textBoxIPAddress.Text, port);
クライアントからサーバへの接続

 TcpClientクラスのコンストラクタのパラメータに接続先サーバのIPアドレスとポート番号を指定してTcpClientオブジェクトを作成すると、.NET Frameworkが自動的にサーバへの接続を確立する。もし、接続できなかった場合は例外が発生するので、きちんと例外処理をする必要がある。

 接続が確立した後は、サーバの場合と同じように、TcpClientオブジェクトからネットワーク・ストリームを取得し、そのストリームに対応するReaderクラスとWriterクラスを作成しておく。

// クライアントがサーバとのデータのやりとりに使うストリームを取得
NetworkStream ns = client.GetStream();
clientReader = new StreamReader(ns, Encoding.UTF8);
clientWriter = new StreamWriter(ns, Encoding.UTF8);
クライアント側でのネットワーク・ストリーム取得とStreamReaderオブジェクト/StreamWriterオブジェクトのセット

 データ受信もサーバの場合と同様で、受信データについてはループを使って受信データがあるかどうかをチェックする。

while (true)
{
  ProcessMessage(clientReader);
}
データの受信待ちループ

 このループもサーバの場合と同様に、別スレッドで動作させる必要がある。ProcessMessageメソッドの詳細は後述する。

■ファイルの送信

 続いては、データの送信/受信の流れについて説明する。データを送信するメソッドとして、ここではSendMessageメソッドを作成した。

// データを送るメソッド
private void SendMessage(StreamWriter writer, string fileName, Byte[] data)
{
  // 設計したプロトコルに従ってファイル名とファイルのデータを

  // エンコードして送信
  writer.WriteLine(ProtocolEncoder.ToBase64EncodedData(fileName, data));
  // 確実にネットワークに流れるようにストリームをFlush
  writer.Flush();
}
データの送信

 このSendMessageメソッドはサーバからでも、クライアントからでも利用できるようになっている。パラメータのwriterに、データを送りたいStreamWriterオブジェクトを指定すると、ファイルが送信されるようになっている。サーバであればserverWriterオブジェクト、クライアントであればclientWriterオブジェクトを指定することになる。パラメータに指定するfileNameは送信したいファイルの名前、dataは送信したいファイルの中身(バイナリ形式)である。

 実際にデータを送信するのは、writerオブジェクトのWriteLineメソッドである。このようにストリームを使うと、コンソールに出力するような手軽さでデータをネットワーク上に書き込むことができる。

 1つ注意しなくてはならないのは、その次の行のFlushメソッドである。ストリームの書き込み先がネットワークやディスク上のファイルのときは、このメソッドが呼ばれて初めて書き込みが実行される。これは書き込みのパフォーマンスを良くするために、アクセス速度の遅い書き込み先に関してはバッファリングが行われるためである。一通りデータの書き込みを行ったら、明示的にFlushメソッドを呼び出して、データの書き込みを実行しておこう。

 WriteLineメソッドのパラメータとして記述しているProtocolEncoder.ToBase64EncodedDataメソッドついて、少し見ておこう。これはファイル名とファイルの中身のデータを基に、データを加工して送信用の1行データを作成するメソッドである。

// ファイル名とファイルの中身からデータ文字列を作成する
public static string ToBase64EncodedData(string filename, Byte[] data)
{
  return filename + "?" + Convert.ToBase64String(data);
}
送信用データ文字列の作成

 .NETのクラス・ライブラリにはBase64を扱うクラスが標準で用意されていることは先に述べたが、Convertクラスがそれで、そのToBase64Stringメソッドではバイト列をBase64でエンコーディングできる。これを用いて、バイナリとして読み込んであるファイルの中身をBase64エンコード文字列に変換し、ファイル名との間に「?」という1文字を挟んで連結している。これでファイル名とファイルの中身の情報をすべて含んだ文字列の完成である。これがネットワーク上でやりとりされるデータになる。

■ファイルの受信

 次に、ファイルの受信について見てみよう。ファイルの受信は、先に出てきたクライアントとサーバの両方で実行される「データ受信待ちループ」により行われる。そのループの中でProcessMessageメソッドを呼び出していたが、実際の受信データの処理はこのメソッドが行う。ProcessMessageメソッドのコードは次のとおりだ。

// 受け取ったメッセージを処理する
// メッセージはProtocolEncodedクラスで決められたプロトコルによってやりとりされる
private void ProcessMessage(StreamReader reader)
{
  lock (this)
  {
    string message = reader.ReadLine();
    string fileName = ProtocolEncoder.GetFileNameFromStreamText(message);
    Byte[] data = ProtocolEncoder.GetDataFromStreamText(message);
    FileIO.WriteFile(Application.StartupPath + "\\" + fileName, data);
    this.textBoxReceivedMessage.BeginInvoke(
      new WriteLineMessageHandler(WriteLineMessage),
      new object[] {fileName + "を受信しました。"});
  }
}
受信データの処理

 ProcessMessageメソッドのパラメータはStreamReader型であり、サーバ側のStreamReaderオブジェクトでもクライアント側のStreamReaderオブジェクトでも受け付けるようになっている。サーバ側、クライアント側の2つの受信用ルーチンが別スレッドで動いているので、lockステートメントを使用してお互いに干渉しないように排他制御を行っている。

 reader.ReadLineメソッドが実際のデータ受信を行う。受信するデータがない場合は、データが読み込み可能になるまでここで待機することになる。データが受信可能な状態になると、受信したデータは文字列messageに格納される。

 データ送信時にはWriteLineメソッドを使ってデータを書き込んだが、これは送信される1つの文字列が1つのファイル・データに相当することを意味する。これに対応して、データ受信の場合では、複数のファイルが転送されてきたときも、ReadLineメソッドを使って1行ずつ、つまりはファイルを1つずつ処理していくことになる。

 ProtocolEncoderクラスのGetFileNameFromStreamTextメソッドは受信したデータ文字列からファイル名を文字列として取り出す作業を行い、GetDataFromStreamTextメソッドはそのファイルの中身をバイナリとして取り出すという作業を行っている。

 最後に、FileIOクラスのWriteFileメソッドを呼び出して、受信したファイル・データをディスクに書き込んでいる。これでネットワークを越えたファイル交換は完了である。

 なお、「this.textBoxReceivedMessage.BeginInvoke……」という部分は、処理内容を表示用のTextBoxコントロールに、受信したファイル名などの情報を表示するためのコードである。通信部分がアプリケーション本体のスレッドとは別スレッドで動作しているため、BeginInvokeメソッドを使用している(詳しくはサンプル・コードを参照してほしい)。

4. おわりに

 本稿では.NETの世界でのTCP/IP通信について解説した。サンプル・プログラムとしては簡易ファイル交換ソフトを作成し、実際にアプリケーションをどのように実装していくかを示した。

 冒頭でも述べたように、もはやありとあらゆるコンピュータがネットワークにつながっている時代である。ネットワーク・プログラミングを行う機会もますます増えてくるだろう。本稿を参考に、ぜひネットワーク・プログラミングに挑戦していただければと思う。End of Article

 

 INDEX
  [特集] .NETネットワーク・プログラミング入門
    1.ネットワーク・プログラミングの概要
    2..NETにおけるソケット通信の技術
    3.簡易ファイル交換プログラムの仕組み
  4..NETにおけるTCP/IP通信処理の実装
 


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間