Condividi tramite


Usare socket per inviare e ricevere dati tramite TCP

Prima di poter usare un socket per comunicare con i dispositivi remoti, il socket deve essere inizializzato con informazioni sul protocollo e sull'indirizzo di rete. Il costruttore per la Socket classe include parametri che specificano la famiglia di indirizzi, il tipo di socket e il tipo di protocollo usati dal socket per stabilire connessioni. Quando si connette un socket client a un socket del 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 rete come IPEndPoint oggetto . Il IPEndPoint è costruito con un IPAddress e il numero di porta corrispondente. Prima di poter avviare una conversazione tramite Socket, è necessario creare 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 in tale dispositivo a cui connettersi. La combinazione di indirizzo di rete e porta del servizio è denominata endpoint, rappresentato in .NET dalla EndPoint classe . Un discendente di EndPoint è definito per ogni famiglia di indirizzi supportata; per la famiglia di 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 di facile utilizzo, ad esempio "host.contoso.com," a un indirizzo Internet numerico (ad esempio 192.168.1.1). GetHostEntryAsync restituisce un oggetto Task<IPHostEntry> che, se atteso, contiene un elenco di indirizzi e alias per il nome richiesto. Nella maggior parte dei casi, è possibile usare il primo indirizzo restituito nella AddressList matrice. Il codice seguente ottiene un oggetto IPAddress contenente l'indirizzo IP per il server host.contoso.com.

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

Suggerimento

A scopo di test e debug manuali, è possibile in genere usare il metodo GetHostEntryAsync con il nome host risultante dal valore Dns.GetHostName() per convertire il nome localhost in un indirizzo IP. Si consideri il frammento di codice seguente:

var hostName = Dns.GetHostName();
IPHostEntry localhost = await Dns.GetHostEntryAsync(hostName);
// This is the IP address of the local machine
IPAddress localIpAddress = localhost.AddressList[0];

Internet Assigned Numbers Authority (IANA) definisce i numeri di porta per i servizi comuni. Per altre informazioni, vedere IANA: Service Name and Transport Protocol Port Number Registry( Registro dei numeri di porta del protocollo di trasporto). Altri servizi possono avere numeri di porta registrati nell'intervallo compreso tra 1.024 e 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 aver scelto una porta da usare per la connessione, l'app può stabilire una connessione con il dispositivo remoto.

Creare un Socket client

Dopo aver creato l'oggetto endPoint , 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 oggetto Socket con la famiglia di indirizzi dell'istanza specifica endPoint, SocketType.Stream e 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 utilizzando Socket.ReceiveAsync.
    • Quando response è un riconoscimento, viene scritto nella console e il ciclo viene interrotto.
  • Infine, il client socket chiama Socket.Shutdown, SocketShutdown.Both che interrompe entrambe le operazioni di invio e ricezione.

Creare un Socket server

Per creare il socket del server, l'oggetto endPoint può restare in ascolto delle 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 oggetto Socket con la famiglia di indirizzi dell'istanza specifica endPoint, SocketType.Stream e ProtocolType.Tcp.

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

  • Il Socket.Listen() metodo viene chiamato per restare in ascolto delle connessioni in ingresso.

  • Il metodo listener chiama Socket.AcceptAsync per accettare una connessione in ingresso sul socket handler.

  • In un while ciclo:

    • Chiama 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 e il server di esempio

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...

Vedere anche