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.ConnectAsync
endPoint
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 zendPoint
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 gniazdachhandler
.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...