SocketAsyncEventArgs.UserToken не обновляется?
В настоящее время у меня есть клиент-серверное приложение, которое включает в себя клиент Silverlight и сервер.NET. Часть.NET использует классы Tcp, предоставленные в пространстве имен System.Net.Sockets, тогда как клиент Silverlight использует необработанные сокеты. Я портирую на это из кода, который в настоящее время использует классы HttpListener, потому что он не соответствует моим потребностям. Классы Http, однако, имеют на стороне SL возможность использовать асинхронные методы стиля Begin* и End*, которые позволяют мне указывать обработчик после завершения операции. У меня проблемы с тем, чтобы заставить это работать с новой системой. Моя текущая стратегия состоит в том, чтобы включить обработчик как часть UserToken. Однако, похоже, что этот токен не обновляется.
Вот некоторый отредактированный код. Я могу заставить обе стороны разговаривать друг с другом, но кажется, что правильный 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 всегда равен нулю.
До сих пор я не смог взломать ProcessSend, поэтому я не вижу, какие именно SocketAsyncEventArgs он отправил. Кто-нибудь знает, почему это событие не запускается (Завершено после отправки)? Я испорчу свою функцию SendToServer?
Я понимаю, что могут быть другие существующие проблемы с синхронизацией и тому подобное, но я не думаю, что это проблема здесь. Я попробовал одну вещь - настроить ManualResetEvent, чтобы никто не отправлял данные на сервер до завершения соединения (ProcessConnect), но это также не решило проблему.
Любая помощь будет оценена!
РЕДАКТИРОВАТЬ: Таким образом, причина этого заключается в том, что когда я вызываю ReceiveAsync в функции ProcessConnect, он используется, когда сервер отправляет ответ для моих данных. Следовательно, UserToken "foo" присутствует в обработчике. В следующий раз, когда сервер отправляет данные, ReceiveAsync использует аргументы с "полосой" UserToken. Так что это немного не синхронизировано, для дуплексной связи. Я не могу гарантировать, что SocketAsyncEventArgs, которые я отправил со стороны клиента, является тем же, который используется в ответе. Кажется, что единственное решение - открыть клиенту SL два сокета - один для данных, инициируемых сервером, а другой для запросов, инициируемых клиентом. Однако это означает, что я не пользуюсь дуплексной природой.
1 ответ
Эта модель не будет работать, потому что я создаю новый SocketAsyncEventArgs при каждой отправке, что означает, что данные могут возвращаться по любому из этих аргументов. Я перешел к модели с пулом SocketAsyncEventArgs, и каждый клиент может иметь только один запрос / ответ за раз.