Usare Socket per inviare e ricevere dati tramite TCP

Prima di usare un socket per comunicare con dispositivi remoti, è necessario inizializzare il socket con informazioni su protocollo e indirizzo di rete. Il costruttore per la classe Socket dispone di parametri che specificano la famiglia di indirizzi, il tipo di socket e il tipo di protocollo usato per stabilire connessioni. Quando si connette un socket client a un socket server, il client userà un IPEndPoint oggetto per specificare l'indirizzo di rete del server.

Creare un endpoint IP

Quando si usa System.Net.Sockets, si rappresenta un endpoint di IPEndPoint rete come oggetto. Viene IPEndPoint costruito con un IPAddress e il relativo numero di porta corrispondente. Prima di poter avviare una conversazione tramite un Socketoggetto , si crea una pipe di dati tra l'app e la destinazione remota.

TCP/IP usa un indirizzo di rete e un numero di porta del servizio per identificare in modo univoco un servizio. L'indirizzo di rete identifica una destinazione di rete specifica; il numero di porta identifica il servizio specifico nel dispositivo a cui connettersi. La combinazione di indirizzi di rete e porta di servizio è denominata endpoint, rappresentato in .NET dalla EndPoint classe . Un discendente di è definito per ogni famiglia di indirizzi supportata; per la famiglia di EndPoint indirizzi IP, la classe è IPEndPoint.

La Dns classe fornisce servizi nome di dominio alle app che usano servizi Internet TCP/IP. Il GetHostEntryAsync metodo esegue una query su un server DNS per eseguire il mapping di un nome di dominio descrittivo (ad esempio "host.contoso.com") a un indirizzo Internet numerico (ad esempio 192.168.1.1). GetHostEntryAsync restituisce un oggetto Task<IPHostEntry> che, quando atteso, contiene un elenco di indirizzi e alias per il nome richiesto. Nella maggior parte dei casi, è possibile usare il primo indirizzo restituito nella matrice AddressList. Il codice seguente ottiene un IPAddress oggetto contenente l'indirizzo IP per il server host.contoso.com.

IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];

Suggerimento

Per scopi di test e debug manuali, è in genere possibile usare il GetHostEntryAsync metodo per ottenere il valore per risolvere il Dns.GetHostName() nome localhost in un indirizzo IP.

Internet Assigned Numbers Authority (IANA) definisce i numeri di porta per i servizi comuni. Per altre informazioni, vedere IANA: Nome del servizio e Registro numero di porta del protocollo di trasporto. Altri servizi possono avere numeri di porta registrati nell'intervallo da 1024 a 65.535. Il codice seguente combina l'indirizzo IP per host.contoso.com con un numero di porta per creare un endpoint remoto per una connessione.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Dopo aver determinato l'indirizzo del dispositivo remoto e scegliendo una porta da usare per la connessione, l'app può stabilire una connessione con il dispositivo remoto.

Creare un Socket client

Con l'oggetto endPoint creato, creare un socket client per connettersi al server. Una volta connesso il socket, può inviare e ricevere dati dalla connessione socket del server.

using Socket client = new(
    ipEndPoint.AddressFamily, 
    SocketType.Stream, 
    ProtocolType.Tcp);

await client.ConnectAsync(ipEndPoint);
while (true)
{
    // Send message.
    var message = "Hi friends 👋!<|EOM|>";
    var messageBytes = Encoding.UTF8.GetBytes(message);
    _ = await client.SendAsync(messageBytes, SocketFlags.None);
    Console.WriteLine($"Socket client sent message: \"{message}\"");

    // Receive ack.
    var buffer = new byte[1_024];
    var received = await client.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    if (response == "<|ACK|>")
    {
        Console.WriteLine(
            $"Socket client received acknowledgment: \"{response}\"");
        break;
    }
    // Sample output:
    //     Socket client sent message: "Hi friends 👋!<|EOM|>"
    //     Socket client received acknowledgment: "<|ACK|>"
}

client.Shutdown(SocketShutdown.Both);

Il codice C# precedente:

  • Crea un'istanza di un nuovo Socket oggetto con una determinata endPoint famiglia di indirizzi delle istanze, l'oggetto SocketType.Streame ProtocolType.Tcp.

  • Chiama il Socket.ConnectAsync metodo con l'istanza endPoint come argomento.

  • In un while ciclo:

    • Codifica e invia un messaggio al server usando Socket.SendAsync.
    • Scrive il messaggio inviato nella console.
    • Inizializza un buffer per ricevere dati dal server usando Socket.ReceiveAsync.
    • Quando l'oggetto response è un riconoscimento, viene scritto nella console e il ciclo viene chiuso.
  • Infine, le client chiamate Socket.Shutdown socket fornite SocketShutdown.Both, che arresta le operazioni di invio e ricezione.

Creare un Socket server

Per creare il socket del server, l'oggetto endPoint può ascoltare le connessioni in ingresso su qualsiasi indirizzo IP, ma è necessario specificare il numero di porta. Dopo aver creato il socket, il server può accettare connessioni in ingresso e comunicare con i client.

using Socket listener = new(
    ipEndPoint.AddressFamily,
    SocketType.Stream,
    ProtocolType.Tcp);

listener.Bind(ipEndPoint);
listener.Listen(100);

var handler = await listener.AcceptAsync();
while (true)
{
    // Receive message.
    var buffer = new byte[1_024];
    var received = await handler.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    
    var eom = "<|EOM|>";
    if (response.IndexOf(eom) > -1 /* is end of message */)
    {
        Console.WriteLine(
            $"Socket server received message: \"{response.Replace(eom, "")}\"");

        var ackMessage = "<|ACK|>";
        var echoBytes = Encoding.UTF8.GetBytes(ackMessage);
        await handler.SendAsync(echoBytes, 0);
        Console.WriteLine(
            $"Socket server sent acknowledgment: \"{ackMessage}\"");

        break;
    }
    // Sample output:
    //    Socket server received message: "Hi friends 👋!"
    //    Socket server sent acknowledgment: "<|ACK|>"
}

Il codice C# precedente:

  • Crea un'istanza di un nuovo Socket oggetto con una determinata endPoint famiglia di indirizzi delle istanze, l'oggetto SocketType.Streame ProtocolType.Tcp.

  • Chiama listener il metodo con l'istanza come argomento per associare il Socket.Bind socket all'indirizzo endPoint di rete.

  • Il Socket.Listen() metodo viene chiamato per ascoltare le connessioni in ingresso.

  • Chiama listener il Socket.AcceptAsync metodo per accettare una connessione in ingresso nel handler socket.

  • In un while ciclo:

    • Chiamate Socket.ReceiveAsync per ricevere dati dal client.
    • Quando i dati vengono ricevuti, vengono decodificati e scritti nella console.
    • Se il response messaggio termina con <|EOM|>, viene inviato un riconoscimento al client usando .Socket.SendAsync

Eseguire il client di esempio e il server

Avviare prima l'applicazione server e quindi avviare l'applicazione client.

dotnet run --project socket-server
Socket server starting...
Found: 172.23.64.1 available on port 9000.
Socket server received message: "Hi friends 👋!"
Socket server sent acknowledgment: "<|ACK|>"
Press ENTER to continue...

L'applicazione client invierà un messaggio al server e il server risponderà con un riconoscimento.

dotnet run --project socket-client
Socket client starting...
Found: 172.23.64.1 available on port 9000.
Socket client sent message: "Hi friends 👋!<|EOM|>"
Socket client received acknowledgment: "<|ACK|>"
Press ENTER to continue...

Vedi anche