<>.NET Socket開発の同期Socket実現2例
17759 ワード
今日は.NETネットワークアプリケーション開発でSocketを同期させるアプリケーションについてお話ししますが、ネットワークアプリケーションのサービス側Socketでは同期Socketを使用すべきではないと考える人が多いようです.はい、ほとんどの場合はそうですが、同期Socketを使用するとより多くの結果が得られるシーンもあります.次の2つのシナリオでは、同期Socketの使用を検討できます.
一、クライアント数が少ない:
数が少ないとは、サーバに同時に接続されるクライアントの数が一般的に50人以下であることを意味します.この場合,同期Socket+Threadを用いてサービス側を実現することを考えることができる.これにより、パフォーマンスが低下することなく、論理的により明確なコードを作成できます.
二、クライアント数が多いが短い接続である:
短い接続とは、クライアントの接続が1回の送受信を処理した後に切断されるシーンであり、例えばHTTPプロトコルは短い接続である.HTTPはクライアントが要求を出す時にSocket接続を確立し、Socketを通じてURL要求を発行し、サービス側はこの要求を処理して対応するページを返送した後にこの接続を切断する.このようなシナリオでは、同期Socketを使用してニーズを実現することもできます.
では、私が上記した2つのニーズを実現すればいいのではないでしょうか.この2つのニーズに対して、私は異なる案を採用してそれらを実現します.
まず最初のニーズを見てみましょう.ここではSocket+Threadを採用して実現します.基本的な流れは以下の通りです.
まずSocketを作成し、EndPointをバインドしてからリスニングを開始します.次に、クライアントからの接続要求を無限ループで受信するスレッドを作成します.リクエストを受信した後、このクライアントのために新しいスレッドを作成し、このスレッドでも無限ループを使用してクライアントからのデータを受信します.コードを見てみましょう.
まず、クライアントをリスニングするためのSocket接続を作成します.
次に、クライアントの接続要求を処理するスレッドを作成します.
最後に、データの受信方法を見てみましょう.
よし、実現全体が完成した. 2つ目のニーズを見てみましょう この案は私たちが別の方法で実現しますが、どうして前の方法で実現しないのですか.分析してみましょう.前の実装では、クライアントにアクセスするたびにスレッドが作成され、大量のクライアントアクセスがあると、スレッドが作成されすぎてしまうことを知っています.しかし、スレッドが多すぎると、Windowsではスレッドのコンテキストを切り替えるのにより多くのCPU時間がかかります(これも前の実装で多くのクライアントにアクセスできない理由です). このスキームでは、各接続が短い接続であることが知られています.しかも順番は決まっています.いずれも、アクセス->受信->送信という順序で、1つの方法で処理全体を完了することができます.これにより、スレッドプールを使用して必要なものを実現することができます.では、コードで話しましょう. まず、クライアントをリスニングするためのSocket接続を作成します.
次に、スレッドプールを作成します.
最後に、スレッドが何をするかを見てみましょう.
どうして私たちはこのようにしますか?
まず,クライアントの接続要求をリスニングするためのSocketを作成し,次に30スレッドのスレッドプールを作成した.各スレッドでAccept、Receive、Send、Close()を実装し、接続、受信、送信、閉じる操作を完了します.
次に、サーバに接続されたクライアントがいると仮定すると、このリクエストにスレッドAcceptがあり、クライアントから送信されたデータの受信を開始し、データを受信した後、クライアントに送信を処理した後、この接続を閉じて、再び接続待ち状態に入ります.他の29スレッドは、このリクエストにAcceptがないため、待機アクセス状態を処理します.
本文の出典:
http://blog.csdn.net/wzd24/archive/2007/10/12/1821386.aspx
一、クライアント数が少ない:
数が少ないとは、サーバに同時に接続されるクライアントの数が一般的に50人以下であることを意味します.この場合,同期Socket+Threadを用いてサービス側を実現することを考えることができる.これにより、パフォーマンスが低下することなく、論理的により明確なコードを作成できます.
二、クライアント数が多いが短い接続である:
短い接続とは、クライアントの接続が1回の送受信を処理した後に切断されるシーンであり、例えばHTTPプロトコルは短い接続である.HTTPはクライアントが要求を出す時にSocket接続を確立し、Socketを通じてURL要求を発行し、サービス側はこの要求を処理して対応するページを返送した後にこの接続を切断する.このようなシナリオでは、同期Socketを使用してニーズを実現することもできます.
では、私が上記した2つのニーズを実現すればいいのではないでしょうか.この2つのニーズに対して、私は異なる案を採用してそれらを実現します.
まず最初のニーズを見てみましょう.ここではSocket+Threadを採用して実現します.基本的な流れは以下の通りです.
まずSocketを作成し、EndPointをバインドしてからリスニングを開始します.次に、クライアントからの接続要求を無限ループで受信するスレッドを作成します.リクエストを受信した後、このクライアントのために新しいスレッドを作成し、このスレッドでも無限ループを使用してクライアントからのデータを受信します.コードを見てみましょう.
まず、クライアントをリスニングするためのSocket接続を作成します.
Socket listener
=
new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint locEP
=
new
IPEndPoint(IPAddress.Any,
2000
);
listener.Bind(locEP);
listener.Listen(
100
);
次に、クライアントの接続要求を処理するスレッドを作成します.
Thread acceptThread
=
new
Thread(
new
ThreadStart(AcceptWorkThread));
acceptThread.Start();
private
void
AcceptWorkThread()
{
Thread.CurrentThread.IsBackground
=
true
;
while
(
true
)
{
Socket accept
=
listener.Accept();
IPEndPoint remoEP
=
(IPEndPoint)accept.RemoteEndPoint;
string
recString
=
"
"
+
remoEP.Address.ToString()
+
"
。
"
;
this
.Invoke(
new
AddListItemHandler(
this
.AddListItem),
new
string
[] { recString });
Thread receiveThread
=
new
Thread(
new
ParameterizedThreadStart(ReceiveWorkThread));
receiveThread.Start(accept);
}
}
最後に、データの受信方法を見てみましょう.
private
void
ReceiveWorkThread(
object
obj)
{
Thread.CurrentThread.IsBackground
=
true
;
Socket socket
=
(Socket)obj;
byte
[] buffer
=
new
byte
[
1024
];
while
(
true
)
{
int
receiveCount
=
socket.Receive(buffer);
if
(receiveCount
>
0
)
{
IPEndPoint remoEP
=
(IPEndPoint)socket.RemoteEndPoint;
string
recString
=
"
"
+
remoEP.Address.ToString()
+
"
:
"
+
Encoding.Default.GetString(buffer,
0
, receiveCount);
this
.Invoke(
new
AddListItemHandler(
this
.AddListItem),
new
string
[] { recString });
socket.Send(buffer, receiveCount, SocketFlags.None);
}
else
{
socket.Close();
break
;
}
}
}
よし、実現全体が完成した. 2つ目のニーズを見てみましょう この案は私たちが別の方法で実現しますが、どうして前の方法で実現しないのですか.分析してみましょう.前の実装では、クライアントにアクセスするたびにスレッドが作成され、大量のクライアントアクセスがあると、スレッドが作成されすぎてしまうことを知っています.しかし、スレッドが多すぎると、Windowsではスレッドのコンテキストを切り替えるのにより多くのCPU時間がかかります(これも前の実装で多くのクライアントにアクセスできない理由です). このスキームでは、各接続が短い接続であることが知られています.しかも順番は決まっています.いずれも、アクセス->受信->送信という順序で、1つの方法で処理全体を完了することができます.これにより、スレッドプールを使用して必要なものを実現することができます.では、コードで話しましょう. まず、クライアントをリスニングするためのSocket接続を作成します.
Socket listener
=
new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint locEP
=
new
IPEndPoint(IPAddress.Any,
2000
);
listener.Bind(locEP);
listener.Listen(
100
);
次に、スレッドプールを作成します.
Thread[] ClientThreadList
=
new
Thread[
30
];
foreach
(Thread th
in
ClientThreadList)
{
th
=
new
Thread(
new
ThreadStart(ClientWorkThread));
th.Start();
}
最後に、スレッドが何をするかを見てみましょう.
private
void
ClientWorkThread()
{
byte
[] buffer
=
new
byte
[
1024
];
while
(
true
)
{
Socket socket
=
listener.Accept();
string
recString
=
"
"
+
remoEP.Address.ToString()
+
"
。
"
;
this
.Invoke(
new
AddListItemHandler(
this
.AddListItem),
new
string
[] { recString });
int
receCount
=
socket.Receive(buffer);
if
(receCount
>
0
)
{
string
recString
=
"
"
+
remoEP.Address.ToString()
+
"
:
"
+
Encoding.Default.GetString(buffer,
0
, receiveCount);
this
.Invoke(
new
AddListItemHandler(
this
.AddListItem),
new
string
[] { recString });
socket.Send(buffer, receCount, SocketFlags.None);
}
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
どうして私たちはこのようにしますか?
まず,クライアントの接続要求をリスニングするためのSocketを作成し,次に30スレッドのスレッドプールを作成した.各スレッドでAccept、Receive、Send、Close()を実装し、接続、受信、送信、閉じる操作を完了します.
次に、サーバに接続されたクライアントがいると仮定すると、このリクエストにスレッドAcceptがあり、クライアントから送信されたデータの受信を開始し、データを受信した後、クライアントに送信を処理した後、この接続を閉じて、再び接続待ち状態に入ります.他の29スレッドは、このリクエストにAcceptがないため、待機アクセス状態を処理します.
本文の出典:
http://blog.csdn.net/wzd24/archive/2007/10/12/1821386.aspx