プログラムの概要
Winsock コントロールを使用すると、リモ ートコンピュータに接続し、データを交換 出来る事を利用して、チャット(通信雑談 システム)を作成する。 サーバー、クライアント共に、背景色が黄 色のテキストボックスに必要項目を入力し、 設定ボタンをクリックすると、通信が確立 し、チャットを開始する事が出来る。 送信用テキストボックスに送信文を入力し て送信ボタンをクリックすると、テキスト が送信される。送信文、受信文共に、受信 用テキストボックスに逐次表示される。猶、 通信状況等は、下部のステータスバーに、其の時の状態が表示される。チャット
C# 2005 ⑤ □ 変数の宣言(データ型 変数名、データ型[ ] 配列変数名) □ 標準コントロールの利用(Label、RadioBotton、Button、Text、Group、StatusBar) □ プロパティの値の取得と設定(Standard: BackColor、Text、Value、Caption) □ プロパティの値の取得と設定(Winsock: State、RemoteHost、RemotePort、LocalIP 等) □ イベントの利用(Load、Click、ConnectionRequest、DataArrival) □ メソッドの利用(Close、Listen、Connect、Accept、SendData、GetData、Focus) □ 日付の利用(DateTime クラス、Now オブジェクト、ToString メソッド)□ 静的クラスのメソッドの利用(Color.FromArgb、Application.Exit、Convert.ToInt16) □ 制御構造構文(条件分岐 if ( ~ ) { … } else { … }、ループ処理 do { … } while ( ~ );) □ 演算子(代入演算子、比較演算子、結合演算子) □ COM コンポーネントの利用(ツールボックスのカスタマイズ、Winsock) 今回の課題項目 □ StatusBar コンポーネントの利用(Panels 等) □ Winsock コンポーネントの利用(Protocol、RemoteHost、RemotePort、Listen、Connect 等) 今回の重点項目 □ 送受信文の画面表示を工夫する。 今回の応用項目
■ ツールボックスのカスタマイズ ■ 今回使用するコントロールの内、Winsock コンポーネントは、標準では、ツールボックスには表示され て居ない。標準でツールボックスに表示されて居ないコンポーネントを追加して使用出来る様にする手 順は、下記の通りで有る。 此処では、ツールボックスのコンポーネントに追加すると仕て、コンポーネント部を開き、余白部分で 右クリックして表示されるポップアップメニューで『ツールボックスのカスタマイズ』をクリックする。
下記のダイアログの『COM コンポーネント』タブで、Microsoft WinSock Control version 6.0 にチェ ックを入れて、OK ボタンをクリックする。
■ オブジェクト・プロパティ一覧 ■ コントロールの種類 プロパティ プロパティの設定値 フォーム Name chat Text チャット FormBorderStyle FixedSingle MaximizeBox False グループボックス1 Name grpSet Text、Font 設定(MS明朝,太字,10) グループボックス2 Name grpSelect Text 空白 ラジオボタン1 Name radServer Text サーバー ラジオボタン2 Name radClient Text クライアント Checked True ラベル1 Name lblLocal Text ローカル ラベル2 Name lblRemote Text リモート ラベル2 ボタン1 ラジオボタン1 グループ1 ラベル3 テキスト1 Winsock ラベル1 ラベル4 ラベル5 グループ2 グループ3 グループ4 テキスト3 テキスト5 ボタン2 テキスト2 テキスト6 テキスト7 テキスト8 ボタン3 ボタン4 ステータスバー ラジオボタン2 テキスト4
コントロールの種類 プロパティ プロパティの設定値 ラベル3 Name lblAddress Text アドレス ラベル4 Name lblPort Text ポート ラベル5 Name lblName Text 名前 テキストボックス1 Name txtAddressL Text 192.168.1.1 テキストボックス2 Name txtAddressR Text 192.168.1.2 テキストボックス3 Name txtPortL Text 1001 テキストボックス4 Name txtPortR Text 1002 テキストボックス5 Name txtNameL Text 空白 テキストボックス6 Name txtNameR Text 空白 ボタン1 Name btnSet Text 設定 グループボックス3 Name grpSx Text 送信 テキストボックス7 Name txtSx ボタン2 Name btnSend Text 送信 グループボックス4 Name grpRx Text 受信 テキストボックス8 Name txtRx MultiLine True ScrollBars Both ソケット Name sckCOM Protocol 0 - sckTCPprotocol ステータスストリップ Name staInfo Items ラベルを3個追加し、下記の様に設定する。
Name staState staDate staTime
AutoSize False
BorderSides All
BorderStyle SunkenOuter
TextAlign MiddleLeft MiddleCenter
Spring True False
Width 任意 100
ボタン3 Name btnClear
Text 消去
ボタン4 Name btnFinish
■ プログラムリスト ■ //============================================================= // 参照の追加:.NET タブで Microsoft.VisualBasic を参照設定する。 //============================================================= using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace chat {
public partial class chat : Form { public chat( ) { InitializeComponent( ); } // フォームが読み込まれた時の処理
private void chat_Load( object sender, EventArgs e ) { // 自機の IP アドレスと名前の取得 txtAddressL.Text = sckCOM.LocalIP; txtNameL.Text = sckCOM.LocalHostName; txtAddressR.Text = sckCOM.LocalIP; // 入力必須項目の指示 txtAddressR.BackColor = Color.FromArgb( 255, 255, 255, 128 ); txtPortR.BackColor = Color.FromArgb( 255, 255, 255, 128 ); // ステータスバーの表示 staState.Text = "未接続";
staDate.Text = DateTime.Now.ToString( "yyyy/MM/dd" ); staTime.Text = DateTime.Now.ToString( "hh:mm:ss" ); }
// ボタン(設定)がクリックされた時の処理
private void btnSet_Click( object sender, EventArgs e ) { // ソケットが開いて居る場合はクローズ if ( sckCOM.CtlState != 0 ) sckCOM.Close( ); 斯うする事に依り、C# からも多 彩なVisual Basic 固有の機能を使 用する事が出来る。 LocalIP プロパティで自機の IP ア ドレスを、LocalHostName プロ パティで自機の名前を取得する事 が出来る。 クライアント側が設定する必要の 有る項目のテキストボックスの背 景色を黄色に仕て居る。 ステータスストリップの各々のラ ベルに情報を表示する。 CtlState プロパティで、ソケット の様々な状態を取得する事が出来 る(0 は開いて居る状態)。 ネームスペース(名前空間)の使 用を宣言して居る。 此の部分は、エディタが、自動的 に記述して下れる。 クラス名と同じ名前のメソッド は、コンストラクタと呼ばれ、ク ラスのインスタンスを生成し、初 期化するメソッドで有る。
if ( radServer.Checked ) {
// サーバの場合
this.Text = "チャット:TCPサーバ";
// ローカルホスト(自機)のポート番号の設定
sckCOM.LocalPort = System.Convert.ToInt16( txtPortL.Text ); // ソケットを作成し接続要求受付モードに移行 sckCOM.Listen( ); // ステータスの表示 staState.Text = "接続受付中"; } else { // クライアントの場合 this.Text = "チャット:TCPクライアント"; // リモートホスト(相手機)のアドレスと名前の設定 sckCOM.RemoteHost = txtAddressR.Text; // リモートホスト(相手機)のポート番号の設定
sckCOM.RemotePort = System.Convert.ToInt16( txtPortR.Text ); // リモートホストへの接続要求
sckCOM.Connect( ); // ステータスの表示
// while ( sckCOM.CtlState != 7 ) Application.DoEvents( ); staState.Text = "接続済"; } } // リモートマシンが接続を要求して来た時の処理
private void sckCOM_ConnectionRequest( object sender,
AxMSWinsockLib.DMSWinsockControlEvents_ConnectionRequestEvent e ) { // サーバで無い場合はプロシージャを強制脱出 if ( !radServer.Checked ) return; // リモートマシンの情報の表示 txtAddressR.Text = sckCOM.RemoteHostIP; txtPortR.Text = sckCOM.RemotePort.ToString( ); // ソケットを一旦閉じて新しい接続の受付 if ( sckCOM.CtlState != 0 ) sckCOM.Close( ); sckCOM.Accept( e.requestID ); // ステータスの表示 staState.Text = "接続-" + sckCOM.RemoteHostIP; } LocalPort プロパティには自機の 使用ポートを指定する。 Listen メソッドは、ソケットを作 成し、接続要求を受け付けるモー ドに移行する。 Connect メソッドは、リモートコ ンピュータへ接続を要求する。 無 限 ル ー プ に 陥 ら 無 い 為 に Application オ ブ ジ ェ ク ト の DoEvents メソッド、プログラム が占有して居る制御をオペレーテ ィングシステムに渡す。 本来は、接続要求後、接続が確立 する迄、待機させるが、接続が確 立しない時の為に、タイムアウト 処理も行う必要が有り、此処では 簡潔化の為、省略した。 サーバー側而巳が必要とする処理 なので、サーバーで無い場合、直 ちにサブプロシージャを抜ける。 RemoteHostIP プロパティで相手 機のIP アドレスを、RemotePort プロパティで相手機のポート番号 を取得する事が出来る。 Accept メソッドで、接続要求を許 可する。 プラス記号(+)は、此の場合、 文字列を結合する演算子で有る。
// ボタン(送信)がクリックされた時の処理
private void btnSend_Click( object sender, EventArgs e ) { string s; byte[ ] b; // 送信テキストに改行を付加して送信 s = txtNameL.Text + ">" + txtSx.Text + "¥r¥n"; sckCOM.SendData( s ); // 送信テキストを一覧に表示 txtRx.Text += s; txtSx.Text = ""; txtSx.Focus( ); } // データを受信した時の処理
private void sckCOM_DataArrival( object sender,
AxMSWinsockLib.DMSWinsockControlEvents_DataArrivalEvent e ) {
byte[ ] b = new byte[ e.bytesTotal ]; string s = ""; object o; // 受信データの取得 o = ( object ) s; sckCOM.GetData( ref o,
( int ) Microsoft.VisualBasic.VariantType.String, sckCOM.BytesReceived ); s = o.ToString( ); /* ===== 参考:此処から ===== while ( sckCOM.BytesReceived > 0 ) { sckCOM.GetData(ref o,
( int ) Microsoft.VisualBasic.VariantType.String, sckCOM.BytesReceived ); s += o.ToString( ); } ===== 参考:此処迄 ===== */ // 送信テキストを一覧に表示 txtRx.Text += s; } SendData メソッドは、データを リモートコンピュータに送信する メソッドで有る。 此処で宣言した変数は宣言したサ ブプロシージャ内でしか値の参照 と設定を行う事が出来ない。 ¥r¥n は、エスケープ文字で、改 行を意味する。 DataArrival イベントは、新しい データが送信されて来た時に発生 するイベントで有る。 GetData メソッドは、現在のデー タブロック(受信データ)を取得 し、其れを変数に格納する。
// ラジオボタン(サーバ)がクリックされた時の処理
private void radServer_Click( object sender, EventArgs e ) { // 入力必須項目の指示 txtPortL.BackColor = Color.FromArgb( 255, 255, 255, 128 ); txtAddressR.BackColor = Color.FromArgb( 255, 255, 255, 255 ); txtPortR.BackColor = Color.FromArgb( 255, 255, 255, 255 ); txtPortL.Focus( ); } // ラジオボタン(クライアント)がクリックされた時の処理 private void radClient_Click( object sender, EventArgs e ) { // 入力必須項目の指示 txtPortL.BackColor = Color.FromArgb( 255, 255, 255, 255 ); txtAddressR.BackColor = Color.FromArgb( 255, 255, 255, 128 ); txtPortR.BackColor = Color.FromArgb( 255, 255, 255, 128 ); txtAddressR.Focus( ); } // ボタン(消去)がクリックされた時の処理
private void btnClear_Click( object sender, EventArgs e ) {
txtRx.Text = ""; }
// ボタン(終了)がクリックされた時の処理
private void btnFinish_Click( object sender, EventArgs e ) { if ( sckCOM.CtlState != 0 ) sckCOM.Close( ); this.Dispose( ); Application.Exit( ); } } } TCP接続で、サーバー側が設定 する項目はLocalPort プロパティ 丈で有る。 TCP接続で、クライアント側が 設定する項目はRemoteHost プロ パティとRemotePort プロパティ で有る。 MultiLine プロパティが真のテキ ストボックスは 32K 迄の入力が 可能。 アプリケーションを終了する場 合、ソケットは閉じてから終了す る事が望ましい。 アプリケーションを終了する場 合、正しくプログラムをメモリか ら消去して終了する。 此処では、通信の基本的な流れを理解する為に、データを受信すると、自動的にイベントが発生す る等、取り扱いが容易な Winsock コントロールをして居る。先ず、此れで、通信の基本的なアー キテクチャ(Architecture:コンピュータのハードウェアやソフトウェアの基本的な設計様式や設 計思想の事)を理解して欲しい。 亦、C#.NET 以降で、本格的に通信プログラムを作成するには、System.Net.Sockets 名前空間の Socket クラスを使用すると良い。此のクラスには、ネットワーク通信の為のメソッドとプロパティ が豊富に用意されて居る。
Winsock コントロール ネットワークサービスへ簡単にアクセスする機能を提供するコントロール Winsock コントロールは、実行時の画面には表示されないコントロールで、TCP ネットワ ークサービスとUDP ネットワークサービスへ簡単にアクセスする機能を提供する。 クライアントアプリケーションやサーバーアプリケーションを記述するのに、プロトコルの詳細を理解 したり、低水準のWinsock API 関数を呼び出したりする必要は無い。此のコントロールが提供する各種 プロパティを設定し、メソッドを呼び出す丈で、簡単にリモートマシンに接続し、送受信両方向のデー タ交換を行う事が出来る。 TCP に関する基本事項 TCP(伝送制御プロトコル)を使用すると、リモートコンピュータとの接続の確立、及び、管理が可能 で有る。接続の両端のコンピュータは、接続を使用して、データを互いにストリーム転送する事が出来 る。 クライアントアプリケーションを作成する場合は、サーバーコンピュータの名前、又は、IP アドレス (RemoteHost プロパティ)、及び、其のサーバーが、接続要求を受け付けて居るポート(RemotePort プロパティ)を知る必要が有る。此等の情報を設定した後、Connect メソッドを呼び出す。 サーバーアプリケーションを作成する場合は、接続要求を受け付けるポート(LocalPort プロパティ) を設定した後、Listen メソッドを呼び出す。クライアントコンピュータが接続を要求すると、 ConnectionRequest イベントが発生する。接続処理を完了させるには、ConnectionRequest イベント内 でAccept メソッドを呼び出す。 接続が確立した後は、孰れのコンピュータもデータの送受信を行う事が出来る。データを送信するには SendData メソッドを呼び出す。亦、データを受信すると、DataArrival イベントが発生する。データ を受け取るには、DataArrival イベント内で GetData メソッドを呼び出す。 UDP に関する基本事項 UDP(ユーザーデータグラムプロトコル)は、コネクションレスプロトコルで有る。TCP での操作と 異なり、コンピュータは接続を確立しない。亦、UDP では、同一のアプリケーションが、クライアン トに成る事も、サーバーにも成る事も出来る。 データを転送するには、先ず、クライアントコンピュータ側のアプリケーションのLocalPort プロパテ ィを設定する。次に、サーバーコンピュータ側のアプリケーションで、RemoteHost プロパティにクラ イアントコンピュータのインターネットアドレスを設定し、RemotePort プロパティに、クライアント コンピュータ側のアプリケーションの LocalPort プロパティと同じ値を設定する。其の後、SendData メソッドを呼び出すと、メッセージの送信が開始される。サーバーコンピュータ側のアプリケーション で必要な操作は此れ丈で有る。次に、クライアントコンピュータのDataArrival イベント内で GetData メソッドを呼び出して、送信されたメッセージを受け取る。 参考:インターネット上で広く使用されて居るハイパーテキスト転送プロトコル(HTTP)及び、ファ イル転送プロトコル(FTP)の2種のインプリメンテーションを提供するインターネットトランスファ (Inet)コントロールも有る。 機 能 解 説
Winsock コントロールの Close メソッドに依るソケットのクローズ ソケットをクローズするメソッド Object.Close( ) TCP 接続、又は、接続要求を受け付けて居るソケットを閉じる。クライアントアプリケーシ ョンでもサーバーアプリケーションでも使用出来る。 オブジェクトにはWinsock コントロールを指定する。 Winsock コントロールの Listen メソッドに依る接続要求の待受 ソケットを作成し、接続要求を受け付けるモードに移行するメソッド Object.Listen( ) TCP 接続に於いてソケットを作成し、接続要求を受け付けるモードに移行して、クライアン トからの接続要求を待ち受ける。此のメソッドは、TCP 接続でしか機能し無い。 オブジェクトにはWinsock コントロールを指定する。 接続要求が送られて来ると、ConnectionRequest イベントが発生する。接続を受け入れるには、 ConnectionRequest イベントのコード内で Accept メソッドを使用する必要が有る。 Winsock コントロールの Connect メソッドに依る接続の要求 リモートコンピュータへの接続を要求するメソッド Object.Connect( 引数1,引数2 ) TCP 接続を確立する際に、リモートコンピュータ(サーバー側)への接続を要求する。 オブジェクトにはWinsock コントロールを指定する。 引数1には、リモートコンピュータの名前を指定し、省略する事が出来る。 引数2には、リモートコンピュータのポートを指定し、省略する事が出来る。 Winsock コントロールの Accept メソッドに依る接続要求の受入 送られて来た接続要求を受け入れるメソッド Object.Accept( 引数 ) TCP 接続に於いて、クライアント側より送られて来た接続要求を受け入れる。此のメソッド は、サーバー側のConnectionRequest イベント内で而巳で使用する事が出来る。 オブジェクトにはWinsock コントロールを指定する。 引数1には、RequestID(ConnectionRequest イベントの引数)を指定し、省略する事は出来ない。 機 能 書 式 解 説 機 能 書 式 解 説 機 能 書 式 解 説 機 能 書 式 解 説
Winsock コントロールの SendData メソッドに依るデータの送信 データをリモートコンピュータに送信するメソッド Object.SendData( 引数 ) 引数で指定したデータをリモートコンピュータに送信する。 オブジェクトにはWinsock コントロールを指定する。 引数には、送信するデータ(数値、又は、文字列)を指定し、必ず指定する。
Visual Basic では、Visual Basic.NET 以降、文字列データを送信する場合は、バイト配列と仕て送信 する必要が有るが、C# では、文字列を、其の儘、送信する事が出来る。 Winsock コントロールの GetData メソッドに依るデータの受信 現在のデータブロックを取得し変数に格納するメソッド Object.GetData( 引数1, 引数2, 引数3 ) リモートコンピュータより受信したデータを引数で指定した変数に格納する。 オブジェクトにはWinsock コントロールを指定する。 引数1には、取得したデータを格納する変数を指定し、必ず指定する。 引数2には、取得するデータの型を指定し、省略する事が出来る。 引数3には、取得するデータのサイズを指定し、省略する事が出来る。
通常は、DataArrival イベント内で、イベントの引数で与えられる bytesTotal を指定して GetData メ ソッドを呼び出す。猶、引数3にbytesTotal より小さい値を指定すると、残りのデータが失われる事を 知らせる10040 番の警告が発生する。 C# では、取得した受信データを格納する変数に、直接 string 型変数を使用する事は出来ず、一旦 object 型変数で取得した後、string 型に変換する必要が有る。下記に受信データを取得する手順を示す。 // 最終的に受信データを格納する string 型変数の宣言 string s = ""; // 暫定的に受信データを格納する object 型変数の宣言 object o;
// string 型変数を object 型にキャストして object 型変数を初期化 o = ( object ) s;
// 受信データを取得して object 型変数に格納
sckCOM.GetData(ref o, (int)Microsoft.VisualBasic.VariantType.String, sckCOM.BytesReceived); // object 型変数を文字列化して string 型変数に代入
s = o.ToString( );
猶、C# では、引数2と引数3も、省略する事は出来ず、必ず指定する必要が有る。亦、Winsock は元々 Visual Basic 6.0 のコンポーネントで有る為、引数2に Microsoft.VisualBasic.VariantType.String の様 に、Visual Basic の機能を使用する必要が有る。 機 能 書 式 解 説 機 能 書 式 解 説
TCP接続の確立の手順 サーバー側 クライアント側 RemotePort プロパティの設定 LocalPort プロパティの設定 Listen メソッドの実行(待受) ConnectionRequest イベント RemoteHost プロパティの設定 Connect メソッドの実行(接続要求) Accept メソッドの実行(受付)
TCP(Transmission Control Protocol)
TCP プロトコルは、コネクション型のプロトコルで有り、電話に例える事が出来る。ユーザ は、接続を確立してから処理を行う必要が有る。亦、一旦接続が確立すると、TCP プロトコ ルでは、接続が持続され、非常に大きなファイル等でもデータの完全性が保証されるが、多 くのリソースが使用される
UDP(User Datagram Protocol)
UDP プロトコルは、コネクションレス型のプロトコルで有り、メモ伝言に例える事が出来る。 1台のコンピュータから別のコンピュータにメッセージが送られるが、両者は明示的に接続 されて居ない。亦、各送信データの最大サイズは、ネットワークに依り決まる。