<>.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接続を作成します.

   
     
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