NetworkStream Asyn c Читать -> Отмена - PullRequest
0 голосов
/ 13 марта 2020

В настоящее время я пытаюсь читать и записывать Asyn c в / из сетевого потока. Мое программное обеспечение является клиентской частью, и сервер может отправлять информацию самостоятельно или отвечать на команды, которые я ему отправляю.

Поэтому мне нужен сокет, который

  • читает все время (в если сервер отправляет информацию о состоянии)
  • прекращает чтение, когда я хочу отправить команды (команды могут быть последовательностями данных с несколькими операциями записи и чтения)

Поэтому я подумал, что это будет хороший подход к созданию семафора и фоновой задачи, которая обрабатывает отправленные сервером сообщения, и в случае, если я хочу отправить команду, я блокирую семафор и получаю полный доступ к операциям чтения / записи в сокет.

Здесь это то, что я делаю в настоящее время.

private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);

public async Task ConnectAsync()
{
    if (_tcpClient.Connected)
    {
        await DisconnectAsync();
    }
    await _tcpClient.ConnectAsync(Hostname, RemotePort);

    //here the background Task is started
    _ = AutoReceiveMessages();
}

private async Task AutoReceiveMessages()
{
    while (_tcpClient.Connected)
    {
        //enter and lock semaphore
        await ClientSemaphore.WaitAsync();
        try
        {
            //read from socket until timeout (ms)
            var msg = await ReadFromSocket(2000);
            foreach (var cmd in SplitMessageInTelegrams(msg))
            {
                Console.WriteLine("MESSAGE --> " + cmd);                    
            }
        }
        catch (Exception ex)
        {
        }
        finally
        {
            //release semaphore
            ClientSemaphore.Release();
        }
    }
}

private async Task<string> ReadFromSocket(double timeout = 0)
{
    var buf = new byte[4096];
    var stream = _tcpClient.GetStream();

    //read from stream or timeout
    var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
    var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));

    await Task.WhenAny(timeoutTask, amountReadTask)
              .ConfigureAwait(false);

    //timeout
    if (!amountReadTask.IsCompleted)
    {
        throw new TimeoutException("Timeout");
    }

    //no timeout
    return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}

Но это не работает, как я ожидал ... Я использую этот метод для отправки сообщения на сервер , и в WireShark я вижу, что сервер резонирует с то же сообщение

protected async Task SendTelegramAsync(ITelegram telegram)
{
    await ClientSemaphore.WaitAsync();
    try
    {
        _ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
        if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");

        var buf = new byte[4096];
        var stream = _tcpClient.GetStream();
        var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");

        Console.WriteLine("WRITE --> " + msg);
        await stream.WriteAsync(msg, 0, msg.Length);

        //comment AutoReceiveMessage and remove comment from this
        //and I get responses from the server
        //var test = await ReadFromSocket(2000);
    }
    finally
    {
        ClientSemaphore.Release();
    }
}

Я знаю, что в этом случае мне не нужен семафор, но позже я хочу создать последовательности, чтобы одна команда состояла из нескольких записей. es и читает, и пока команда выполняется, я не хочу использовать метод AutoReceiveMessages.

Теперь проблема заключается в

  • Если я использую его таким образом, я никогда не получу В ответ метод ReadFromSocket всегда получает тайм-аут, даже когда Wireshark сообщает мне, что сервер ответил
  • Но даже лучше, если я отключу AutoReceiveMessages (просто прокомментируем _ = AutoReceiveMessages ()) и использую ReadFromSocket непосредственно в SendTelegramAsyn c () все работает, как ожидалось.

Так что я думаю, что проблема связана с фоновой задачей и ReadAsyn c, но я не мог понять это ...

1 Ответ

0 голосов
/ 13 марта 2020

Понял!

stream.DataAvailable - ваш друг (или мой друг :)).

Если я проверю перед ReadAsyn c, если DataIsAvailable, у меня больше нет проблем.

if (_tcpClient.GetStream().DataAvailable)
    var msg = await ReadFromSocket(DEFAULT_TIMEOUT);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...