TCPサーバーは、SocketAsyncEventArgsを使用して開発され、Windowsサービスと同じ非同期メソッドです。20 SocketAsyncEventArgsで1秒あたりの受信数
ThreadPool.SetMaxThreads(15000, 30000);
ThreadPool.SetMinThreads(10000, 20000);
そして、両方とも真(戻り値が記録されます)を返します。 2000〜3000のクライアントがこのサーバーにメッセージを送信し始め、接続を受け入れるようになります(接続数がカウントされ、予想通りです - 接続プールがあります)。サーバープロセスのスレッド数は〜2050〜3050に増加します。ここまでは順調ですね!
ReceivedAsyncがtrueを返すか、SocketAsyncEventArgsのCompletedイベントの後に呼び出されるReceivedメソッドがあります。
ここで問題が始まります:どのくらいのクライアントが接続され、どれくらいのメッセージを送信しても、Receivedは最大20回も呼び出されます。クライアントの数が増えると、この数(20)は〜10に減少します。
環境:TCPサーバーとクライアントが同じマシン上でシミュレートされています。 2台のCPUと4GBのRAMを搭載し、もう1台は8コアのCPUと12GBのRAMを搭載した2台のマシンでこのコードをテストしました。データが失われることはありませんが(まだ)、受信操作ごとに1つ以上のメッセージが受信されることがあります。それはいいです。しかし、受信動作の数をどのように増やすことができますか?
実装に関するその他の注意事項:コードは大きく、さまざまなロジックが含まれています。全体的な説明は次のようになります。新しい接続を受け入れるための単一のSocketAsyncEventArgsがあります。それは素晴らしい作品です。新しい受け入れられた接続ごとに、データを受け取るための新しいSocketAsyncEventArgsを作成します。私はこの1つ(受信用に作成されたSocketAsyncEventArgs)をプールに入れます。これは再利用されませんが、UserTokenは接続の追跡に使用されています。切断された接続や、7分間データを送信していない接続が閉じられて破棄されます(SocketAsyncEventArgsのAcceptSocketはシャットダウン(両方)され、閉じられ、破棄され、SocketAsyncEventArgsオブジェクト自体も同様です)。ここではこれらのタスクを実行しますが、他のすべてのロジック、ロギングおよびエラーチェックや他の何かが、それは単純明快にするために削除され須藤クラスが(多分、問題のあるコードを発見することが容易である)である:
class Sudo
{
Socket _listener;
int _port = 8797;
public Sudo()
{
var ipEndPoint = new IPEndPoint(IPAddress.Any, _port);
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_listener.Bind(ipEndPoint);
_listener.Listen(100);
Accept(null);
}
void Accept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += AcceptCompleted;
}
else acceptEventArg.AcceptSocket = null;
bool willRaiseEvent = _listener.AcceptAsync(acceptEventArg); ;
if (!willRaiseEvent) Accepted(acceptEventArg);
}
void AcceptCompleted(object sender, SocketAsyncEventArgs e)
{
Accepted(e);
}
void Accepted(SocketAsyncEventArgs e)
{
var acceptSocket = e.AcceptSocket;
var readEventArgs = CreateArg(acceptSocket);
var willRaiseEvent = acceptSocket.ReceiveAsync(readEventArgs);
Accept(e);
if (!willRaiseEvent) Received(readEventArgs);
}
SocketAsyncEventArgs CreateArg(Socket acceptSocket)
{
var arg = new SocketAsyncEventArgs();
arg.Completed += IOCompleted;
var buffer = new byte[64 * 1024];
arg.SetBuffer(buffer, 0, buffer.Length);
arg.AcceptSocket = acceptSocket;
arg.SocketFlags = SocketFlags.None;
return arg;
}
void IOCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
Received(e);
break;
default: break;
}
}
void Received(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success || e.BytesTransferred == 0 || e.Buffer == null || e.Buffer.Length == 0)
{
// Kill(e);
return;
}
var bytesList = new List<byte>();
for (var i = 0; i < e.BytesTransferred; i++) bytesList.Add(e.Buffer[i]);
var bytes = bytesList.ToArray();
Process(bytes);
ReceiveRest(e);
Perf.IncOp();
}
void ReceiveRest(SocketAsyncEventArgs e)
{
e.SocketFlags = SocketFlags.None;
for (int i = 0; i < e.Buffer.Length; i++) e.Buffer[i] = 0;
e.SetBuffer(0, e.Buffer.Length);
var willRaiseEvent = e.AcceptSocket.ReceiveAsync(e);
if (!willRaiseEvent) Received(e);
}
void Process(byte[] bytes) { }
}
MB RAM?それは私のキャッシュですか?私はあなたに数GBのメモリがあることを願っています。ボトルネックを探すためにResource MonitorまたはProcess Explorerを使用しましたか? – HABO
うーん...あなたのサーバー上の非同期アーキテクチャとは何ですか?どのように聴き方が起こり、接続がどのように受け入れられるかを記述する疑似コードを投稿することができますか? –
@HABOありがとう!私はそれを修正しました。 –