現在、Silverlight クライアントと .NET サーバーを含むクライアント サーバー アプリケーションを使用しています。.NET 部分は System.Net.Sockets 名前空間で提供される Tcp クラスを使用しますが、Silverlight クライアントは raw ソケットを使用します。私のニーズに合わないため、現在 HttpListener クラスを使用しているコードからこれに移植しています。ただし、Http クラスには SL 側で Begin* および End* スタイルの非同期メソッドを使用する機能があり、操作が完了したらハンドラーを指定できます。これを新しいシステムで動作させるのに問題があります。私の現在の戦略は、ハンドラーを UserToken の一部として含めることです。ただし、このトークンは更新されていないようです。
ここにいくつかの編集されたコードがあります。2 つの側を相互に通信させることはできますが、正しい UserToken が送信されていないようです。
public class ClientUserToken
{
public Handler Handler { get; set; }
public string Test { get; set; }
public ClientUserToken(Handler handler, string test)
{
Handler = handler;
Test = test;
}
}
public class SocketClient
{
private Socket _clientSocket;
private string _ipAddress;
private int _port;
private void OpenSocket()
{
var endPoint = new DnsEndPoint(_ipAddress, _port);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(null, "foo");
args.RemoteEndPoint = endPoint;
args.Completed += OnSocketCompleted;
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.ConnectAsync(args);
}
void OnSocketCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect: ProcessConnect(e); break;
case SocketAsyncOperation.Receive: ProcessReceive(e); break;
case SocketAsyncOperation.Send: ProcessSend(e); break; // this never gets called
}
}
void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
byte[] response = new byte[1024];
e.SetBuffer(response, 0, response.Length);
_clientSocket.ReceiveAsync(e);
}
}
void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
var userToken = e.UserToken as ClientUserToken; // this token is always the one set in ProcessConnect
// process the data
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is never called, for some reason
void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var userToken = e.UserToken as ClientUserToken;
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is the public API that users use to actually send data across
public void SendToServer(byte[] data, int len, Handler handler)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(handler, "bar");
args.SetBuffer(data, 0, len);
if (!_clientSocket.SendAsync(args))
{
ProcessReceive(args);
}
}
}
上記のコメントが示すように、ProcessReceive では、userToken.Test は常に "foo" であり、userToken.Handler は常に null です。
これまで ProcessSend に侵入できなかったため、実際に送信された SocketAsyncEventArgs を確認できません。このイベントが発生しない理由を知っている人はいますか (送信後に完了)? SendToServer 関数を台無しにしていませんか?
同期などには他にも既存の問題がある可能性があることは承知していますが、それはここの問題ではないと思います。私が試したことの1つは、ManualResetEventを設定して、接続が完了する前に誰もサーバーにデータを送信しないようにすることでした(ProcessConnect)が、それでも問題は解決しませんでした.
どんな助けでも大歓迎です!
編集:これが発生する理由は、ProcessConnect 関数で ReceiveAsync を呼び出すと、サーバーがデータの応答を送り返すときに使用されているためです。したがって、UserToken "foo" がハンドラーに存在します。次にサーバーがデータを送信するとき、ReceiveAsync は UserToken "bar" で引数を使用します。したがって、二重通信ビットについては、一種の同期が取れていません。クライアント側から送信した SocketAsyncEventArgs が、応答で使用されるものと同じであることを保証できません。唯一の解決策は、SL クライアントに 2 つのソケット (1 つはサーバーが開始したデータ用、もう 1 つはクライアントが開始した要求用) を開くことです。ただし、これは二重の性質を利用していないことを意味します。