Yuukari
@Yuukari
.NET and web developer

Как реализовать асинхронный прием сокетов в C#?

Приветствую. В данный момент практикуюсь в использовании сокетов, появилась мысль сделать приложение, подобное TeamViewer'у (удаленное управление). Написал клиент и сервер, но смог реализовать только синхронную работу с сокетами.

Проблема в том, что мне нужно после подключения клиента отправить к нему запрос с сервера (например, о получении основной информации о ПК), и потом получить от него ответ. То есть, клиент должен сначала принять сообщение с сервера, дать на него ответ, и при этом остаться подключенным к серверу, чтобы в любой момент времени можно было найти этот клиент по его ID, отправить ему новый запрос и принять ответ.

Сервер реализовал через TcpListener, принимаю сообщения от клиентов следующим образом:
while (isListening)
{
    try
    {
        TcpClient tcpClient = listener.AcceptTcpClient();
        Client client = new Client(tcpClient, this);
    }
    catch (SocketException ex) when (ex.ErrorCode == 10004)
    {
        return;
    }
    catch (Exception ex) when (!debugMode)
    {
        Logger.WriteException("Socket Server", "Error: ", ex);
    }
}


Отправка сообщений клиенту реализована через следующий метод в классе Client:
public byte[] SendMessage(SocketMessage socketMessage)
{
    byte[] message = SocketMessage.Build(socketMessage);

    NetworkStream clientStream = client.GetStream();

    byte[] request = message;
    clientStream.Write(request, 0, request.Length);

    byte[] response = new byte[65536];
    int responseLength;

    do
    {
        responseLength = clientStream.Read(response, 0, response.Length);
    }
    while (clientStream.DataAvailable);

    return response;
}

Собственно, этот метод вызывается в конструкторе класса, чтобы после его создания сразу отправить запрос на получение базовой информации о ПК (ОС, имя пользователя, IP адрес и т.д.)

Клиентское приложение принимает запросы и отвечает на них следующим образом:
while (true)
{
    clientStream = client.GetStream();

    byte[] request = new byte[1024];
    int requestLength = 0;
    StringBuilder requestBuilder = new StringBuilder();

    requestLength = clientStream.Read(request, 0, request.Length);
    Console.WriteLine("Received a request from " + client.Client.LocalEndPoint.ToString() + ", " + request.Length + " bytes");
    responseMessage = ResponseToMessage(request);

    Console.Write("Sending response to " + client.Client.LocalEndPoint.ToString() + ", " + responseMessage.Length + " bytes");

    byte[] response = responseMessage;
    clientStream.Write(response, 0, response.Length);

    Console.WriteLine(" OK");
}


Но как перенести это и заставить работать асинхронно я без малейшего понятия. Пытался читать документацию, но ничего не понял. Везде примеры классической реализации, где сервер ждет запроса от клиента, и отвечает на него. Мне же нужно наоборот, отправить с сервера запрос на клиент, и получить от него ответ.
  • Вопрос задан
  • 1620 просмотров
Пригласить эксперта
Ответы на вопрос 2
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Пытался читать документацию, но ничего не понял. Везде примеры классической реализации, где сервер ждет запроса от клиента, и отвечает на него. Мне же нужно наоборот, отправить с сервера запрос на клиент, и получить от него ответ.

После того как соединение установлено сокеты на его концах абсолютно равноправны. Нет различия между клиентом и сервером. Делай сервер так, как делал бы клиента. Простейший вариант -- рождать отдельный поток для каждого соединения и делать в нём всё синхронно.
Ответ написан
Комментировать
petermzg
@petermzg
Самый лучший программист
// один раз инициализировали
var tcpListener = new TcpListener(IPAddress.Any, 8090);
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(OnAcceptTCPClient, null); // Начали ждать коннектов
// На коннект клиента
private void OnAcceptTCPClient(IAsyncResult ar)
{
   var client = tcpListener.EndAcceptTcpClient(ar);
   new ClientReceiver(client); // Здесь будем асинхронно получать данные от клиентов.
   lock (clients)
       clients.Add(client); // сохранили для отправок
   // начинаем ждать новых коннектов
   tcpListener.BeginAcceptTcpClient(OnAcceptTCPClient, null);
}
ClientReceiver(TcpClient tcpClient) {
       buffer = new byte[READ_BUFFER_SIZE];
       stream = tcpClient.GetStream();
       asyncResult = stream.BeginRead(buffer, 0, READ_BUFFER_SIZE, ReadStreamCallback, null);
}
// когда данные пришли
private void ReadStreamCallback(IAsyncResult ar)
{
      int readed;
      readed = stream.EndRead(ar);
      AddData(buffer, readed); // обрабатываем, что получили
      // запускаем новое ожидание
      asyncResult = stream.BeginRead(buffer, 0, READ_BUFFER_SIZE, ReadStreamCallback, null);
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы