この知恵ノートを「知恵コレクション」に追加しました。
追加した知恵ノートはMy知恵袋の「知恵コレクション」ページで確認できます。
「知恵コレクション」に登録済みです。
再登録しました。
追加に失敗しました。
ノートに戻り、もう一度やり直してください。
すでに1,000件のノートが登録されています。
新しく追加したい場合は、My知恵袋の「知恵コレクション」ページで登録されているノートを削除してください。
追加できませんでした。
ノートは削除されました。
知恵コレに追加する:2人
C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)
ライター:abiko_tetuさん(最終更新日時:3日前)投稿日:2012/1/1 アドバイス受付中!
- ナイス!:
10
- 閲覧数:7039
- 付箋(アドバイス)指数中→
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
「C# で ソケット通信のサーバー用プログラムを作成する方法」における一連の説明(知恵ノート)において、ここでは、「C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)」について説明します。
注意
- ※ <ソケットサーバーの種類について>
- ここで取り上げるサンプルは、ソケットサーバーの学習用のサンプルコードです。すなわち、サーバーは、どのようなコードになるかを伝えることを目的にしています。よって、なるべく単純な構造になっています。なお、本サンプルでも、LAN範囲での単純なチャット程度には使えると思います。しかし、より実用的なサーバーシステムを作るには、後に用意しますサンプルを参考にして下さい。
総合の目次
本ページを含めた関連事項の総合目次です。
http://note.chiebukuro.yahoo.co.jp/detail/n1654
参考
本サンプルと、特に関連性のあるサンプルを下記に記します。
それ以外の関連サンプルは、上記の総合目次を参照して下さい。
<関連サンプル>
サンプル 「C# で ソケット通信の基礎的サーバー作成(Socket + Socket)」
http://note.chiebukuro.yahoo.co.jp/detail/n1656
サンプル 「C# で ソケット通信の基礎的サーバー作成(TcpListener + TcpClient)」
(本サンプル)
サンプル 「C# で ソケット通信の基礎的サーバー作成(TcpListener + Socket)」
http://note.chiebukuro.yahoo.co.jp/detail/n1658
<関連性の内容>
本サンプルは、なるべく単純な構造にしたサーバー用のサンプルです。上記の関連サンプルも同様に、単純なサーバーですので本質は同じです。
違いは、Visual C# から用意されているクラスが、違う種類を使っているだけですので、サーバーとしての機能の本質は同じです。
はじめに
ここでは、Visual C# の Socketクラスで、基礎的なサーバーを作成する方法について解説します。
正確には、
(1) サーバーとしての主となる処理をTcpListenerクラスを使い、
(2) 実際にクライアントへ電文を送受信する処理部は、TcpClientクラスを使います。
送受信部のスレッド化への対応/未対応
本サンプルでは、スレッド処理部は、最小限におさえています。本サンプルの目的は、サーバーがどのようなコードになるのか、学習することが主目的だかれです。
なお、クライアントからの受信受け付けをする部分は、無限ループになりますので、その箇所だけスレッド処理にしました。無限ループは、処理を占有してしまうので、プログラム全体が動作しなくなるからです。
なお、後に用意しますサンプルでは、電文処理部(受信処理をするコードと、送信するコードの部分)もスレッド処理にします。
サーバー機能の主制御用クラスの種類
ソケットサーバーを作るために Visual C# から用意されているクラスは、Socketクラス と TcpListenerクラスがあります。
Socketクラスは、名前の通り、ソケット通信のためのクラスです。
これに対して、TcpListenerクラスは、Socketクラスよりも若干簡単にプログラムを書けるようにしたクラスです。
本サンプルでは、TcpListenerクラスのほうを使っています。
クライアントへの対処を直接行なう処理で使うクラスの種類
上記クラスのオブジェクトとは別に、クライアントへの直接の対処を行なうための、通信用オブジェクトを用意します。
すなわち、その通信用オブジェクトは、クライアントから送られてきた電文を実際に処理するために使われます。
また、クライアントへ返信を返す処理も、そのオブジェクトを使います。
その通信用オブジェクトのクラスは、Socketクラス と TcpClientクラスがあります。
本サンプルでは、TcpClientクラスのほうを使っています。
(なお、TcpClientクラスは、TcpListenerクラス用に用意されたクラスです。よって、本サンプルでは、TcpClientクラスのほうを使います。しかし、Socketクラス も使えますので、別のサンプルでSocketクラスの場合を紹介します)
本サンプルの種類のまとめ
学習目的なので、なるべく単純な構造になっています。
すなわち、マルチスレッド化は、最小限におさえている(受信受け付けの無限ループの箇所をスレッド化しているだけ)。
サーバーの通信機能としての主制御は、TcpListenerクラスを用いる。
クライアントへの対処をするクラスは(すなわち、実際に電文の送受信をするコードを書くためのクラスは)、TcpClientクラスを用いる。
サンプルコード
こで取り上げるサンプルは、なるべく複雑にならない範囲で、ある程度は、実際に通信を確かめられるものとします。
本サンプの仕様
ボタンは、「サーバー開始」ボタンと「サーバー終了」ボタンと言う2個のボタンを用意しています。
「サーバー開始」ボタンを押すと、受信受け付けの無限ループが、動作開始となります。
「サーバー終了」ボタンを押せば、受信受け付けの無限ループは終了します。
サーバー開始の状態で、クライアントから電文が送られてきたら、その電文の内容をテキストボックスに表示します。
なお、サンプルコードのIPアドレスは、127.0.0.1とし、ポート番号は、9000にしています。よって、サンプルコードを試す場合は、実際に使うサーバーのIPアドレスに変更して下さい。
フォームデザイン等の前準備
コードを記述する前に、フォームのデザイン作成などの、以下の前準備を行なって下さい。
<プロジェクトの作成>
本サンプルの確認用に、新規にプロジェクトを作成して下さい。
プロジェクトの種類は、「Windowsフォームアプリケーション」です。
<フォームのデザイン>
デザイン画面で、Label(ラベル)を1個と、TextBox (テキストボックス) を1個と、 Button (ボタン)を2個貼り付けて下さい。
<イベントプロシージャの作成>
デザイン画面のフォームをダブルクリックして、Form1_Load() メソッドを作って下さい。
次に、デザイン画面のフォームが選択された状態で、「プロパティ」画面の「イベント」ボタン(雷マークのボタン)をクリックして下さい。
その「プロパティ」画面で、FormClosed と言う項目をダブルクリックして下さい。
そうすると、Form1_FormClosed()メソッドが作成されます。
また、先程貼り付けたデザイン画面上のボタン「button1」をダブルクリックして、button1_Click() メソッド を作って下さい。
同様にボタン「button2」もダブルクリックして、button2_Click() メソッド を作って下さい。
コード
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ServerByTcpListener
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// スレッドからテキストボックスをアクセスすることを指定
Control.CheckForIllegalCrossThreadCalls = false;
}
//============
// メンバー変数
private System.Net.Sockets.TcpListener server; // リスナー(接続待ちや受信等を行なうオブジェクト)
private System.Threading.Thread ListeningCallbackThread; // 接続待ちスレッド
private volatile bool SLTAlive; // 接続待ちスレッド終了指示フラグ(volatile が指定されていることに注意)
//============
// フォーム起動時イベント
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "サーバー"; // フォームのタイトル名
button1.Text = "サーバー開始"; // 開始ボタンの表示文字
button2.Text = "サーバー終了"; // 終了ボタンの表示文字
label1.Text = ""; // 状態表示用ラベルを初期化
// スレッド終了指示フラグを未終了に初期化
SLTAlive = false;
}
//============
// フォーム閉鎖時イベント
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// サーバアプリを終了するにもかかわらず、接続待ちスレッドを終了していない場合の処理
if (SLTAlive)
{
// スレッド終了指示フラグを終了に設定
SLTAlive = false;
// 接続要求受け入れの終了
server.Stop();
// 念のためスレッドをnull設定
ListeningCallbackThread = null;
}
}
//============
// 接続待ち開始ボタンのクリックイベント
private void button1_Click(object sender, EventArgs e)
{
if (!SLTAlive) // まだ接続待ちスレッドを生成していない場合
{
// 接続待ち用スレッドを作成
ListeningCallbackThread = new System.Threading.Thread(ListeningCallback);
// 接続待ち用スレッドを開始
ListeningCallbackThread.Start();
// スレッド終了指示フラグを未終了に設定
SLTAlive = true;
}
}
//============
// 接続待ち終了ボタンのクリックイベント
private void button2_Click(object sender, EventArgs e)
{
if (SLTAlive) // 接続待ちスレッドが作成されていて使える場合
{
if (server != null)
{
// 接続要求受け入れの終了
server.Stop();
}
// スレッド終了指示フラグを終了に設定
SLTAlive = false;
label1.Text = "サーバー終了";
}
}
//============
// 接続待ちスレッド用メソッド
private void ListeningCallback()
{
// リスナー(接続要求受け入れ待機)を生成
server = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9000);
// *** server = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 9000);
// 接続要求受け入れ開始
server.Start();
label1.Text = "サーバー開始";
try
{
// 受信の受付を行なうための無限ループ
while (SLTAlive) // スレッド終了指示フラグでの終了指示がある場合はループ終了
{
// 受信接続キュー内で、接続待ちがあるか判断
if (server.Pending() == true)
{
// クライアントからの接続を受け付ける
System.Net.Sockets.TcpClient ClientSocket = server.AcceptTcpClient(); // TCPクライアント
// 通信ストリームの取得
System.Net.Sockets.NetworkStream stream = ClientSocket.GetStream();
// クライアントからの電文の受信
byte[] ReceiveData = new byte[2000];
int DataLength = stream.Read(ReceiveData, 0, ReceiveData.Length); // 電文の列長
string str = System.Text.Encoding.Unicode.GetString(ReceiveData, 0, DataLength);
textBox1.Text = str; // 受信データ
// 返信電文をクライアントへ送信
byte[] SendBuffer = System.Text.Encoding.Unicode.GetBytes("本サーバーの御利用ありがとう御座います。");
stream.Write(SendBuffer, 0, SendBuffer.Length);
stream.Flush(); // フラッシュ(強制書き出し)
// TCPクライアントをクローズ
ClientSocket.Close();
}
// 短時間だけ待機
System.Threading.Thread.Sleep(100);
}
}
catch (Exception ex)
{
label1.Text = "サーバー終了";
}
}
}
}
実行結果
コードを書きましたら、その後は、実際にビルドして実行してみて下さい。
なお、本サンプルは、サーバー用プログラムです。クライアント用プログラムは、別途用意する必要があります(総合目次から、クライアント用プログラムを参照)。
備考
http://note.chiebukuro.yahoo.co.jp/detail/n1808
さいごに
ここでは、サーバー用プログラムを実際に試すことが、主要目的になっています。
なお、サーバーシステムの作成は、いろいろと難しい技術が必要です。
そのことから、初心者や中級者の中には、敷居が高い存在に感じる人もいると思います。
よって、本サンプルでは、なるべく単純にした通信プログラムで、実際に確かめてもらうことを目的にしています。
どのような様子のものかを、最初のさわり程度でも知っていれば、次のステップへ進みやすいと思います。
アドバイス(このノートのライターへのメッセージ)を送る
このノートはどうでしたか? いいと思ったことや、こうしたらもっとよくなるといったメッセージを送りましょう! ノートの内容やライターについて質問がある場合は、Q&Aから質問してみましょう
アドバイスを送るには、
Yahoo! JAPAN IDでのログインおよび
Yahoo!知恵袋の利用登録が必要です。
感想アドバイス履歴
このノートに関するQ&A
このノートに関するQ&Aは、まだありません。
あなたにおすすめの知恵ノート
あなたにおすすめのQ&A
- このC#をCに直していただけませんか? using System; using System.Collections.Generic; using Sys...
- C言語でソケット通信のプログラムを組んだんですが、セグメント違反になる理由がわかりません…どなた...
- ソケット通信のプログラムを書いていて、実行結果が hello(1)1 hello(2)2 … hello(100)100 となるよ...
- ソケット通信のaccpet関数に関して 今、私は、マルチクライアント対応HTTPサーバを自作しています。...
- javaのソケット通信の受信で、InputStreamの終了が認識出来ません。 (1)下記のネット上で見つけたチ...