#c_sharp #async_programming #async_await
Уже долго пытаюсь найти хотя бы какой-то пример, но пока безуспешно, поэтому обращаюсь с просьбой к вам. Проблема такова. Хочу написать простое асинхронное клиент-серверное приложение с банальной передачей байт. Удалось найти и реализовать 2 решения: колбэки и await/async базирующиеся на tcplistener и networkStream. Но это все не то. Пытаюсь написать базируясь на Task, Sockets и async/await без tcplistener и networkStream. Почитал некоторую литературу, но необходимого ответа/примера не нашел. Знаю, что делается это базируясь на следующем: public static Task ConnectAsync(this Socket socket, EndPoint remoteEP) { return Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, remoteEP, null); } но пока ничего не выходит. Даже сервер не могу "собрать" воедино. Прошу помощи. Обновление Удалось подключиться.А вот с передачей пока не получается. private readonly Socket _server; public ServerSocket(IPAddress ipAddress, int port) { _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _server.Bind(new IPEndPoint(ipAddress, port)); _server.Listen(20); Console.WriteLine("Wait connection"); Accept(); } private async void Accept() { for (;;) { var socket = await Task.Factory.FromAsync < Socket > (_server.BeginAccept, _server.EndAccept, true); Console.WriteLine("Connected"); } }
Ответы
Ответ 1
Для начала, правильный путь — это именно async/await. По поводу работы с сокетами, вот есть хороший обзор. Итак, создаём серверный сокет: public ServerSocket(IPAddress ipAddress, int port) { _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _server.Bind(new IPEndPoint(ipAddress, port)); _server.Listen(20); } Здесь мы создали сокет, но основная работа у нас не мгновенна, поэтому выносим её в метод Run. TaskAcceptAsync() { return Task .Factory.FromAsync( _server.BeginAccept, _server.EndAccept, null); } public async Task Run() { while (true) { var client = await AcceptAsync(); var clientServeTask = ServeClient(client); // что дальше делать с этим заданием - ваше дело // например, вы можете просто записать его в список } } ConnectAsync для сервера не нужен. В ServeClient инстанциируеем объект, представляющий клиента: async Task ServeClient(Socket client) { using (var c = new Client(client)) await c.RunAsync(); } В объект Client лежит собственно логика вашего сервера. byte[] greeting = Encoding.UTF8.GetBytes("Hi, I'm server ru.SO/2015"); public async Task RunAsync() { await SendAsync(greeting); var result = await ReceiveAsync(20); // ... } (Возможно, вам захочется вернуть результат.) Ну и вспомогательные процедуры: Task TrySendAsync(byte[] buffer, int offset, int size, SocketFlags flags) { return Task .Factory.FromAsync( _clientSocket.BeginSend, _clientSocket.EndSend, buffer, offset, size, flags, null); } Task SendAsync(byte[] buffer) { int sent = 0; int remaining = buffer.Length; while (remaining > 0) { var partSent = await TrySendAsync(data, sent, remaining, SocketFlags.None); sent += partSent; remaining -= partSent; } } Не забудьте закрыть клиентский сокет в Dispose. Да, наверняка вам захочется суметь оборвать работу сервера. Для этого вам стоит протянуть CancellationToken'ы через Task'и. Ответ 2
Могу посоветовать пример из другого вопроса - https://ru.stackoverflow.com/a/516728/192004 . Там создается 10 клиентов, сервер поочередно принимает их и отправляет ответы. Сервер работает в одном потоке (точнее, в одной Task), клиенты могут работать в разных потоках. Всё на async/await + куча советов.
Комментариев нет:
Отправить комментарий