SocketAsyncEventArgs.UserToken не обновляется? - PullRequest
1 голос
/ 30 сентября 2011

В настоящее время у меня есть клиент-серверное приложение, которое включает в себя клиент 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 всегда имеет значение null.

До сих пор я не смог взломать ProcessSend, поэтому не вижу, какие именно SocketAsyncEventArgs он отправил.Кто-нибудь знает, почему это событие не запускается (Завершено после отправки)?Я испорчу свою функцию SendToServer?

Я понимаю, что могут быть другие существующие проблемы с синхронизацией и тому подобное, но я не думаю, что это проблема здесь.Я попытался настроить вручную ManualResetEvent, чтобы никто не отправлял данные на сервер до завершения соединения (ProcessConnect), но это также не решило проблему.

Любая помощь будет принята с благодарностью.!

РЕДАКТИРОВАТЬ: Таким образом, причина, по которой это происходит, заключается в том, что когда я вызываю ReceiveAsync в функции ProcessConnect, он используется, когда сервер отправляет ответ для моих данных.Следовательно, UserToken "foo" присутствует в обработчике.В следующий раз, когда сервер отправляет данные, ReceiveAsync использует аргументы с «полосой» UserToken.Так что это немного не синхронизировано для бита дуплексной связи.Я не могу гарантировать, что SocketAsyncEventArgs, которые я отправил со стороны клиента, является тем же, который используется в ответе.Кажется, что единственное решение - открыть клиенту SL два сокета - один для данных, инициируемых сервером, а другой для запросов, инициируемых клиентом.Однако это означает, что я не пользуюсь дуплексной природой.

1 Ответ

0 голосов
/ 06 октября 2011

Эта модель не будет работать, потому что я создаю новый SocketAsyncEventArgs при каждой отправке, что означает, что данные могут возвращаться по любому из этих аргументов.Я перешел к модели с пулом SocketAsyncEventArgs, и каждый клиент может иметь только один запрос / ответ за раз.

...