Страницы

Поиск по вопросам

вторник, 17 марта 2020 г.

Sockets client+server with await/async c# 5.0

#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. Task AcceptAsync() { 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 + куча советов.

Комментариев нет:

Отправить комментарий