Używanie gniazd do wysyłania i odbierania danych za pośrednictwem protokołu TCP

Zanim będzie można użyć gniazda do komunikowania się z urządzeniami zdalnymi, gniazdo musi zostać zainicjowane przy użyciu informacji o protokole i adresie sieciowym. Konstruktor klasy Socket ma parametry, które określają rodzinę adresów, typ gniazda i typ protokołu używany przez gniazdo do nawiązywania połączeń. Podczas łączenia gniazda klienta z gniazdem serwera klient użyje IPEndPoint obiektu w celu określenia adresu sieciowego serwera.

Tworzenie punktu końcowego adresu IP

Podczas pracy z usługą System.Net.Socketsreprezentujesz punkt końcowy sieci jako IPEndPoint obiekt. Obiekt IPEndPoint jest skonstruowany z numerem IPAddress i odpowiadającym mu numerem portu. Zanim będzie można zainicjować konwersację za pośrednictwem elementu , należy utworzyć potok danych między aplikacją Socketa zdalnym miejscem docelowym.

Protokół TCP/IP używa adresu sieciowego i numeru portu usługi, aby jednoznacznie zidentyfikować usługę. Adres sieciowy identyfikuje określone miejsce docelowe sieci; numer portu identyfikuje konkretną usługę na tym urządzeniu do nawiązania połączenia. Połączenie adresu sieciowego i portu usługi jest nazywane punktem końcowym, który jest reprezentowany na platformie .NET przez klasę EndPoint . Element potomny elementu jest definiowany dla każdej obsługiwanej EndPoint rodziny adresów; dla rodziny adresów IP klasa to IPEndPoint.

Klasa Dns udostępnia usługi nazw domen dla aplikacji korzystających z usług internetowych TCP/IP. Metoda GetHostEntryAsync wysyła zapytanie do serwera DNS, aby zamapować przyjazną dla użytkownika nazwę domeny (na przykład "host.contoso.com") na numeryczny adres internetowy (na przykład 192.168.1.1). GetHostEntryAsync Zwraca element Task<IPHostEntry> , który w przypadku oczekiwania zawiera listę adresów i aliasów dla żądanej nazwy. W większości przypadków można użyć pierwszego adresu zwróconego w tablicy AddressList . Poniższy kod pobiera IPAddress adres IP serwera host.contoso.com.

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

Napiwek

Do celów testowania ręcznego i debugowania zazwyczaj można użyć GetHostEntryAsync metody z wynikową nazwą hosta z Dns.GetHostName() wartości, aby rozpoznać nazwę hosta lokalnego na adres IP. Rozważmy następujący fragment kodu:

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

Urząd IANA (Internet Assigned Numbers Authority) definiuje numery portów dla typowych usług. Aby uzyskać więcej informacji, zobacz IANA: Service Name and Transport Protocol Port Number Registry (IANA: nazwa usługi i rejestr numerów portów protokołu transportu). Inne usługi mogą mieć zarejestrowane numery portów w zakresie od 1024 do 65 535. Poniższy kod łączy adres IP z host.contoso.com numerem portu w celu utworzenia zdalnego punktu końcowego dla połączenia.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Po określeniu adresu urządzenia zdalnego i wybraniu portu do użycia dla połączenia aplikacja może nawiązać połączenie z urządzeniem zdalnym.

Tworzenie Socket klienta

Po utworzeniu endPoint obiektu utwórz gniazdo klienta, aby nawiązać połączenie z serwerem. Po nawiązaniu połączenia gniazdo może wysyłać i odbierać dane z połączenia gniazda serwera.

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

Poprzedni kod języka C#:

  • Tworzy wystąpienie nowego Socket obiektu z daną endPoint rodziną adresów wystąpień, SocketType.Stream, i ProtocolType.Tcp.

  • Wywołuje metodę Socket.ConnectAsyncendPoint z wystąpieniem jako argumentem.

  • while W pętli:

    • Koduje i wysyła komunikat do serwera przy użyciu polecenia Socket.SendAsync.
    • Zapisuje wysłaną wiadomość do konsoli programu .
    • Inicjuje bufor do odbierania danych z serwera przy użyciu polecenia Socket.ReceiveAsync.
    • response Gdy element jest potwierdzeniem, jest zapisywany w konsoli, a pętla jest zamykana.
  • Na koniec wywołania Socket.Shutdown gniazda podane SocketShutdown.Both, client które zamyka zarówno operacje wysyłania, jak i odbierania.

Tworzenie Socket serwera

Aby utworzyć gniazdo serwera, obiekt może nasłuchiwać połączeń przychodzących na dowolnym adresie IP, endPoint ale należy określić numer portu. Po utworzeniu gniazda serwer może akceptować połączenia przychodzące i komunikować się z klientami.

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

Poprzedni kod języka C#:

  • Tworzy wystąpienie nowego Socket obiektu z daną endPoint rodziną adresów wystąpień, SocketType.Stream, i ProtocolType.Tcp.

  • Metoda listener wywołuje metodę Socket.Bind z endPoint wystąpieniem jako argumentem, aby skojarzyć gniazdo z adresem sieciowym.

  • Metoda jest wywoływana Socket.Listen() w celu nasłuchiwania połączeń przychodzących.

  • Metoda listener wywołuje metodę Socket.AcceptAsync w celu akceptowania połączenia przychodzącego w gniazdach handler .

  • while W pętli:

    • Wywołuje metodę Socket.ReceiveAsync odbierania danych od klienta.
    • Po odebraniu danych są dekodowane i zapisywane w konsoli.
    • response Jeśli komunikat zakończy się ciągiem <|EOM|>, potwierdzenie zostanie wysłane do klienta przy użyciu polecenia Socket.SendAsync.

Uruchamianie przykładowego klienta i serwera

Najpierw uruchom aplikację serwera, a następnie uruchom aplikację kliencka.

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

Aplikacja kliencka wyśle komunikat na serwer, a serwer odpowie potwierdzeniem.

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

Zobacz też