Freigeben über


Verwenden von Sockets zum Senden und Empfangen von Daten über TCP

Bevor Sie einen Socket für die Kommunikation mit Remotegeräten verwenden können, muss der Socket mit Protokoll- und Netzwerkadresseninformationen initialisiert werden. Der Konstruktor für die Socket Klasse verfügt über Parameter, die die Adressfamilie, den Sockettyp und den Protokolltyp angeben, den der Socket zum Herstellen von Verbindungen verwendet. Beim Verbinden eines Clientsockets mit einem Serversocket verwendet der Client ein IPEndPoint Objekt, um die Netzwerkadresse des Servers anzugeben.

Erstellen eines IP-Endpunkts

Wenn Sie mit System.Net.Sockets arbeiten, stellen Sie einen Netzwerkendpunkt als ein IPEndPoint-Objekt dar. Der IPEndPoint wird mit einer IPAddress und der entsprechenden Portnummer erstellt. Bevor Sie über einen Socket eine Konversation initiieren können, müssen Sie eine Datenpipeline zwischen Ihrer App und dem Remoteziel erstellen.

TCP/IP verwendet eine Netzwerkadresse und eine Dienstportnummer zur eindeutigen Identifizierung eines Diensts. Die Netzwerkadresse identifiziert ein bestimmtes Netzwerkziel und die Portnummer den Dienst auf diesem Gerät, mit dem eine Verbindung hergestellt werden soll. Die Kombination von Netzwerkadresse und Dienstport wird Endpunkt genannt. Dieser wird in .NET durch die EndPoint-Klasse dargestellt. Ein Nachfolger des EndPoint wird für jede unterstützte Adressfamilie definiert. Die Klasse für die IP-Adressfamilie ist IPEndPoint.

Die Dns-Klasse stellt Domain Name Services für Apps bereit, die TCP/IP-Internetdienste verwenden. Die GetHostEntryAsync-Methode fragt einen DNS-Server ab, um einer numerischen Internetadresse (z. B. 192.168.1.1) einen benutzerfreundlichen Domänennamen (z. B. „host.contoso.com“) zuzuordnen. GetHostEntryAsync gibt einen Task<IPHostEntry> zurück. Wenn dieser erwartet wird, enthält er eine Liste der Adressen und Aliasse für den angeforderten Namen. In den meisten Fällen können Sie die erste Adresse verwenden, die im AddressList-Array zurückgegeben wurde. Der folgende Code erhält eine IPAddress mit der IP-Adresse für den Server host.contoso.com.

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

Tipp

Zu manuellen Test- und Debugzwecken können Sie in der Regel die GetHostEntryAsync-Methode mit dem resultierenden Hostnamen aus dem Dns.GetHostName()-Wert verwenden, um den Localhostnamen in eine IP-Adresse aufzulösen. Betrachten Sie den folgenden Codeausschnitt:

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) definiert die Portnummern für verbreitete Dienste. Weitere Informationen finden Sie unter IANA: Portnummerregister für Dienstnamen und Transportprotokolle. Andere Dienste haben registrierte Portnummern im Bereich von 1024 bis 65.535. Der folgende Code kombiniert die IP-Adresse für host.contoso.com mit einer Portnummer zum Erstellen eines Remoteendpunkts für eine Verbindung.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Nach der Ermittlung der Adresse des Remotegeräts und der Auswahl eines Ports für die Verbindung, kann die App versuchen, eine Verbindung mit dem Remotegerät herzustellen.

Erstellen eines Socket Clients

Erstellen Sie mit dem endPoint erstellten Objekt einen Clientsocket, um eine Verbindung mit dem Server herzustellen. Sobald der Socket verbunden ist, kann er Daten von der Serversocketverbindung senden und empfangen.

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

Der vorhergehende C#-Code:

  • Instanziiert ein neues Socket-Objekt mit einer angegebenen endPoint-Instanzadressfamilie, SocketType.Stream und ProtocolType.Tcp

  • Ruft die Socket.ConnectAsync-Methode mit der endPoint-Instanz als Argument auf.

  • In einer while-Schleife:

    • Codiert und sendet eine Nachricht mithilfe von Socket.SendAsync an den Server.
    • Schreibt die gesendete Nachricht in die Konsole.
    • Initialisiert einen Puffer zum Empfangen von Daten vom Server mithilfe von Socket.ReceiveAsync.
    • Wenn response eine Bestätigung ist, wird sie in die Konsole geschrieben und die Schleife wird beendet.
  • Schließlich ruft der client-Socket Socket.Shutdown auf, wenn SocketShutdown.Both gegeben ist, wodurch sowohl Sende- als auch Empfangsvorgänge beendet werden.

Erstellen eines Socket Servers

Zum Erstellen des Serversockets kann das endPoint Objekt auf eingehende Verbindungen an einer beliebigen IP-Adresse lauschen, die Portnummer muss jedoch angegeben werden. Nachdem der Socket erstellt wurde, kann der Server eingehende Verbindungen akzeptieren und mit Clients kommunizieren.

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|>"
}

Der vorhergehende C#-Code:

  • Instanziiert ein neues Socket-Objekt mit einer angegebenen endPoint-Instanzadressfamilie, SocketType.Stream und ProtocolType.Tcp

  • Die listener Methode wird mit der Socket.BindendPoint Instanz als Argument aufgerufen, um den Socket der Netzwerkadresse zuzuordnen.

  • Die Socket.Listen() Methode wird aufgerufen, um auf eingehende Verbindungen zu lauschen.

  • Die listener Methode ruft die Socket.AcceptAsync Methode auf, um eine eingehende Verbindung im handler Socket zu akzeptieren.

  • In einer while-Schleife:

    • Ruft Socket.ReceiveAsync auf, um Daten vom Client zu empfangen.
    • Wenn die Daten empfangen werden, wird sie decodiert und in die Konsole geschrieben.
    • Wenn die response Nachricht mit <|EOM|> endet, wird eine Bestätigung durch Socket.SendAsync an den Client gesendet.

Ausführen des Beispielclients und des Servers

Starten Sie zuerst die Serveranwendung, und starten Sie dann die Clientanwendung.

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

Die Clientanwendung sendet eine Nachricht an den Server, und der Server antwortet mit einer Bestätigung.

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

Siehe auch