none
Async\await Чтение мешает подключению? Вопрос по асинхронным операциям RRS feed

  • Вопрос

  • Я решил попробовать async\await в результате чего появился ниже приведенный код. Это готовое консольное приложение, где сервер ожидает подключения на порту, и два клиента подключаются к серверу. Суть проблемы в том, что сервер не хочет подключать обоих клиентов а только одного. Но если закоментировать строку 105 сервер подключает обоих клиентов но тогда не может читать(Эта строка отмечена коментарием '// Comment here'). И я не пойму как сделать, что бы работали обе функции.

    using System;
    using System.Collections.Generic;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static List<NetworkStream> list;
            static NetworkStream clientStream1;
            static NetworkStream clientStream2;
    
            static void Main(string[] args)
            {
                list = new List<NetworkStream>();
    
                StartServer();
                ConnectAsTcpClient1();
                ConnectAsTcpClient2();
    
                while (true)
                {
                    var str = Console.ReadLine();
    
                    if (str.Length > 0)
                    {
                        if (str[0] == '1')
                        {
                            ServerSendMessage(str);
                        }
                        else if (str[0] == '2')
                        {
                            ClientSendMessage1(str);
                        }
                        else if (str[0] == '3')
                        {
                            ClientSendMessage2(str);
                        }
                    }
                }
            }
    
            static async void ConnectAsTcpClient1()
            {
                using (var tcpClient = new TcpClient())
                {
                    Console.WriteLine("[Client1] Connecting to server");
    
                    await tcpClient.ConnectAsync("127.0.0.1", 1234);
                    
                    clientStream1 = tcpClient.GetStream();
    
                    var buffer = new byte[4096];
    
                    while (true)
                    {
                        var byteCount = await clientStream1.ReadAsync(buffer, 0, buffer.Length);
                        var response = Encoding.UTF8.GetString(buffer, 0, byteCount);
                        Console.WriteLine("[Client1] Server response was {0}", response);
                    }
                }
            }
    
            static async void ConnectAsTcpClient2()
            {
                using (var tcpClient = new TcpClient())
                {
                    Console.WriteLine("[Client2] Connecting to server");
    
                    await tcpClient.ConnectAsync("127.0.0.1", 1234);
    
                    clientStream2 = tcpClient.GetStream();
    
                    var buffer = new byte[4096];
    
                    while (true)
                    {
                        var byteCount = await clientStream2.ReadAsync(buffer, 0, buffer.Length);
                        var response = Encoding.UTF8.GetString(buffer, 0, byteCount);
                        Console.WriteLine("[Client2] Server response was {0}", response);
                    }
                }
            }
    
            static TcpListener tcpListener;
    
            static async void StartServer()
            {
                tcpListener = TcpListener.Create(1234);
                tcpListener.Start(100);
    
                await StartListener();
            }
    
            static async Task StartListener()
            {
                while (true)
                {
                    var tcpClient = await tcpListener.AcceptTcpClientAsync();
    
                    Console.WriteLine("[Server] Client has connected");
    
                    await StartServerPoint(tcpClient); // Comment here
                }
            }
    
            static async Task StartServerPoint(TcpClient tcpClient)
            {
                var serverStream = tcpClient.GetStream();
    
                list.Add(serverStream);
    
                var buffer = new byte[4096];
    
                while (true)
                {
                    var byteCount = await serverStream.ReadAsync(buffer, 0, buffer.Length);
                    var request = Encoding.UTF8.GetString(buffer, 0, byteCount);
    
                    Console.WriteLine("[Server] Client wrote {0}", request);
                }
            }
    
    
            static async void ServerSendMessage(string str)
            {
                var buf = Encoding.UTF8.GetBytes(str);
    
                for (int i = 0; i < list.Count; ++i)
                {
                    await list[i].WriteAsync(buf, 0, buf.Length);
                }
            }
    
            static async void ClientSendMessage1(string str)
            {
                var buf = Encoding.UTF8.GetBytes(str);
    
                await clientStream1.WriteAsync(buf, 0, buf.Length);
            }
    
            static async void ClientSendMessage2(string str)
            {
                var buf = Encoding.UTF8.GetBytes(str);
    
                await clientStream2.WriteAsync(buf, 0, buf.Length);
            }
        }
    }





    • Изменено Max Charp 20 апреля 2014 г. 12:20
    20 апреля 2014 г. 12:16

Ответы

  • Дело в том, что в указанной строке код останавливается и ждет завершения StartListener(). Убрав await вы позволили данному методу выполняться параллельно, что как бы решило ситуацию.

    IMHO по всему вы не понимаете разницу между async/await и multi-thread. В данной ситуации лучше использовать параллельные потоки.

    • Помечено в качестве ответа Max Charp 20 апреля 2014 г. 15:30
    20 апреля 2014 г. 15:23
  • Нет. Если тело такого метода синхронное, то и вызов будет синхронным. Этим пользуются, например, если результат был закэширован в локальной переменной. Тогда нет необходимости на создание отдельного потока.

    Асинхронным вызов станет только после первого await внутри этого метода (который станет ожидать следующий вызванный асинхронный метод) или же создания Task. Тут важно понимать, что до этой точки исходный код все еще будет выполняться синхронно. И, например, Sleep(100000) в нем тормознет именно вызывающий поток.

    Поймите главное - задача await как раз дождаться завершения асинхронного вызова. Поэтому ваш цикл все время ждет завершения обработки каждого соединения по отдельности.


    • Изменено AndreyVeselovMVP 21 апреля 2014 г. 3:33
    • Предложено в качестве ответа YatajgaEditor 21 апреля 2014 г. 14:05
    • Помечено в качестве ответа Max Charp 21 апреля 2014 г. 18:22
    21 апреля 2014 г. 3:32

Все ответы

  • Похоже я нашел решение, но почему так работает я так и не понял. Если в указанной строчке убрать await то все работает
    20 апреля 2014 г. 14:43
  • Дело в том, что в указанной строке код останавливается и ждет завершения StartListener(). Убрав await вы позволили данному методу выполняться параллельно, что как бы решило ситуацию.

    IMHO по всему вы не понимаете разницу между async/await и multi-thread. В данной ситуации лучше использовать параллельные потоки.

    • Помечено в качестве ответа Max Charp 20 апреля 2014 г. 15:30
    20 апреля 2014 г. 15:23
  • Получается если метод помечен как async он в любом случае вызывается асинхронно?
    20 апреля 2014 г. 15:34
  • Нет. Если тело такого метода синхронное, то и вызов будет синхронным. Этим пользуются, например, если результат был закэширован в локальной переменной. Тогда нет необходимости на создание отдельного потока.

    Асинхронным вызов станет только после первого await внутри этого метода (который станет ожидать следующий вызванный асинхронный метод) или же создания Task. Тут важно понимать, что до этой точки исходный код все еще будет выполняться синхронно. И, например, Sleep(100000) в нем тормознет именно вызывающий поток.

    Поймите главное - задача await как раз дождаться завершения асинхронного вызова. Поэтому ваш цикл все время ждет завершения обработки каждого соединения по отдельности.


    • Изменено AndreyVeselovMVP 21 апреля 2014 г. 3:33
    • Предложено в качестве ответа YatajgaEditor 21 апреля 2014 г. 14:05
    • Помечено в качестве ответа Max Charp 21 апреля 2014 г. 18:22
    21 апреля 2014 г. 3:32