仕事のために、プロトコルとして TCP を使用して最大 8000/10000 のアクティブな接続を処理できるサーバーを作成する必要があります。
以前に IOCP を使用したことがありますが、高負荷の後にサーバーがイベントを失い始めるという奇妙な状況が発生しています。
アーキテクチャをテストするために、受信したすべてのものを送信するだけのクライアントとサーバーを作成しました。これは最大 1000/2000 のクライアントで正常に動作しますが、より多くの接続を取得し始めると、IOCP コールバックが呼び出されません!
サーバーとクライアントがローカル マシンで実行されている
1 ミリ秒の送受信イベントの途中に Thread.Sleep を配置すると、すべて正常に動作しますが、サーバーが処理できるネットワーク スループットは大幅に低下します。私は Thread.Yield と Thread.Sleep(0) を試しました。これは Yieald に非常に似ていますが、有用な結果はありませんでした。
私の開発マシンは 8 GB の RAM を搭載した i7-3770 であるため、この負荷を処理できます。Thread.Sleep を使用すると、CPU 消費量は 30% 未満であり、イベントを失うことなく最大 10000 の接続を処理できますが、200mbps (クライアント + サーバー) のスループットで処理できます...システムがイベントを失わない場合、スレッドはありません。スリープ、サーバーは最大 8 Gbps のスループットを処理できます!
8192 のバッファ サイズを使用しています。
最良の状態で最適化するために、私のコードは必要なすべての読み取りバッファーを事前に初期化し、SocketAsyncEventArgs の読み取り/書き込みを行います (以下のコードでは初期化がありますが、以下のコードを拡張して必要なものをすべて事前に初期化するスーパー クラスがあります。
ここにサーバー部分のコードがあります
public bool SendAsync(byte[] Buffer, int Offset, int Length)
{
// Verifica se è connesso
if (this.IsConnected == false)
{
return false;
}
// Attende un eventuale invio in corso
this.sendAsyncAutoResetEvent.WaitOne();
if (this.sendSocketAsyncEventArgs == null)
{
// Inizializza l'oggetto contenente le informazioni per la gestione asincrona (IOCP)
// dell'invio dei dati
this.sendSocketAsyncEventArgs = new SocketAsyncEventArgs();
this.sendSocketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(
this.SocketAsyncCallback);
}
try
{
this.sendSocketAsyncEventArgs.SetBuffer(Buffer, Offset, Length);
}
catch (ObjectDisposedException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (NullReferenceException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
return this.SendAsyncInternal();
}
private bool SendAsyncInternal()
{
try
{
// Prova ad avviare la ricezione asincrona
if (this.socket.SendAsync(this.sendSocketAsyncEventArgs) == false)
{
// L'operazione si è completata in modo sincrono, gestisce gli eventi
this.SocketAsyncCallback(this, this.sendSocketAsyncEventArgs);
}
}
catch (SocketException e)
{
// Imposta l'eccezione
this.lastSocketException = e;
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (ObjectDisposedException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (NullReferenceException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
return true;
}
protected void SocketAsyncCallback(object sender, SocketAsyncEventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.ProcessSocketEvent), e);
}
private void ProcessSocketEvent(object State)
{
SocketAsyncEventArgs e = State as SocketAsyncEventArgs;
try
{
if
(
// Richiesta chiusura della connessione
(
e.LastOperation != SocketAsyncOperation.Connect
&&
e.SocketError == SocketError.Success
&&
e.BytesTransferred == 0
)
||
e.SocketError == SocketError.OperationAborted
||
e.SocketError == SocketError.ConnectionReset
)
{
// Termina la connessione
this.Close();
}
else if (e.SocketError == SocketError.ConnectionRefused)
{
this.ProcessSocketConnectRefused(e);
}
else
{
// Verifica la presenza di eventuali errori
if (e.SocketError != SocketError.Success)
{
throw new Exception(e.SocketError.ToString());
}
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
this.ProcessSocketConnect(e);
break;
case SocketAsyncOperation.Receive:
this.ProcessSocketReceive(e);
break;
case SocketAsyncOperation.Send:
this.ProcessSocketSend(e);
this.sendAsyncAutoResetEvent.Set();
break;
default:
break;
}
}
}
catch (ObjectDisposedException)
{
// do nothing
}
catch (NullReferenceException)
{
// do nothing
}
}
private void ProcessSocketConnect(SocketAsyncEventArgs e)
{
this.OnConnectionEstablished();
}
private void ProcessSocketConnectRefused(SocketAsyncEventArgs e)
{
this.OnConnectionRefused();
}
private void ProcessSocketReceive(SocketAsyncEventArgs e)
{
this.lastPacketRecievedAt = DateTime.Now;
this.OnDataReceived(e.Buffer, e.Offset, e.BytesTransferred);
}
private void ProcessSocketSend(SocketAsyncEventArgs e)
{
this.lastPacketSendedAt = DateTime.Now;
this.OnDataSended(e.Buffer, e.Offset, e.BytesTransferred);
}
ご協力いただきありがとうございます!
編集
オペレーティング システムが Windows 8 64Bit であることを指定するのを忘れていました