Несколько соединений с TcpClient, второе соединение всегда зависает / ничего не делает - PullRequest
0 голосов
/ 24 ноября 2018

Итак, у меня есть консоль TcpClient, которая прослушивает порт 9096. Я хочу, чтобы клиент мог обрабатывать несколько соединений (одновременных или нет).Я также не хочу использовать темы.Я хочу использовать async / await.Мне также нужно иметь возможность корректно закрывать приложение во время определенных событий, стараясь не потерять какие-либо данные.Поэтому мне нужен токен отмены.У меня в основном работает код, но есть две проблемы:

Во-первых, когда приложение начинает слушать, и я отправляю ему данные;все работает правильно, пока отправитель использует одно и то же начальное соединение с приложением.Как только новое соединение (или, я полагаю, сокет? Не понятно по терминологии) установлено, приложение не обрабатывает новые данные.

Во-вторых, когда приложению подается сигнал завершения и токен отменяетсяприложение не закрывается.Я не получаю никаких исключений и не могу понять, что я делаю неправильно.

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

Я в своем уме, пытаясь понять этои исчерпали все мои идеи.

РЕДАКТИРОВАТЬ: Я переместил AcceptTcpClientAsync в цикл, как предложено ниже, и это ничего не изменило.Приложение работает так же, как и раньше.

Program.cs

class Program
{
    private static List<Task> _listeners = new List<Task>();
    private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();

    static void Main(string[] args)
    {
        Console.TreatControlCAsInput = false;
        Console.CancelKeyPress += (o, e) => {
            Console.WriteLine("Shutting down.");
            cancelSource.Cancel();
        };            

        Console.WriteLine("Started, press ctrl + c to terminate.");

        _listeners.Add(Listen(cancelSource.Token));

        cancelSource.Token.WaitHandle.WaitOne();
        Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
    }
}

Прослушивание

public async Task Listen(CancellationToken token){
    var listener  = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
    listener.Start();

    Console.WriteLine("Listening on port 9096");

    while (!token.IsCancellationRequested) {
        // Also tried putting AcceptTcpClientAsync here.
        await Task.Run(async () => {
            var client = await listener.AcceptTcpClientAsync();
            using (var stream = client.GetStream())
            using (var streamReader = new StreamReader(stream, Encoding.UTF8))
            using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
                while (!token.IsCancellationRequested) {    
                    // DO WORK WITH DATA RECEIVED
                    vat data = await streamReader.ReadAsync();
                    await streamWriter.WriteLineAsync("Request received.");
                }
            }
        });
    }

    Console.WriteLine("Stopped Accepting Requests.");
    listener.Server.Close();
    listener.Stop();
}

1 Ответ

0 голосов
/ 24 ноября 2018

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

var cancellation = new CancellationTokenSource();

...

var listener = new TcpListener(...);
listener.Start();
try
{
    while (!token.IsCancellationRequested)
    {
        var client = await  listener.AcceptTcpClientAsync()
        ...
    }
}
finally
{
    listener.Stop();
}

// somewhere in another thread
cancellation.Cancel();

Обновление

Я попробовал это, и никакое поведение не изменилось.Все еще не устанавливает соединение после первого.

 await ...
 while (!token.IsCancellationRequested) {    
      // DO WORK WITH DATA RECEIVED

Очевидно, что AcceptTcpClientAsync никогда не будет вызван снова, потому что вы ожидаете задачу.Этот метод - то, что принимает клиента, если вы не можете вызвать его, вы больше не получаете клиентов.

Вы не можете заблокировать здесь, что вы и делаете.Пожалуйста, ознакомьтесь с некоторыми примерами сокет-серверов, чтобы лучше понять, как написать слушатель

...