Y
Y
Yuukari2019-06-06 01:24:23
C++ / C#
Yuukari, 2019-06-06 01:24:23

How to implement asynchronous socket reception in C#?

Greetings. At the moment I'm practicing using sockets, I had an idea to make an application similar to TeamViewer (remote control). I wrote a client and a server, but I could only implement synchronous work with sockets.

The problem is that after connecting the client, I need to send a request to it from the server (for example, about obtaining basic information about the PC), and then receive a response from it. That is, the client must first receive a message from the server, give a response to it, and at the same time remain connected to the server so that at any time you can find this client by its ID, send it a new request and receive a response.

The server implemented through TcpListener, I receive messages from clients as follows:

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);
    }
}


Sending messages to the client is implemented through the following method in the Client class:
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;
}

Actually, this method is called in the class constructor in order to immediately send a request for obtaining basic information about the PC (OS, username, IP address, etc.) after its creation.

The client application receives requests and responds to them as follows:
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");
}


But how to transfer it and make it work asynchronously, I have no idea. I tried to read the documentation, but did not understand anything. Everywhere examples of classical implementation, where the server waits for a request from the client, and responds to it. On the contrary, I need to send a request from the server to the client, and receive a response from it.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
J
jcmvbkbc, 2019-06-06
@jcmvbkbc

I tried to read the documentation, but did not understand anything. Everywhere examples of classical implementation, where the server waits for a request from the client, and responds to it. On the contrary, I need to send a request from the server to the client, and receive a response from it.

After the connection is established, the sockets at its ends are absolutely equal. There is no distinction between client and server. Do the server the way you would do the client. The simplest option is to spawn a separate thread for each connection and do everything in it synchronously.

P
Peter, 2019-06-06
@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);
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question