Sdílet prostřednictvím


Přehled protokolu TCP

Důležité

Třída Socket je vysoce doporučena pro pokročilé uživatele místo TcpClient a TcpListener.

Pro práci s protokolem TCP (Transmission Control Protocol) máte dvě možnosti: buď použijte Socket pro maximální kontrolu a výkon, nebo použijte pomocné třídy TcpClient a TcpListener. TcpClient a TcpListener jsou postaveny na System.Net.Sockets.Socket třídě a starají se o podrobnosti přenosu dat pro snadné použití.

Třídy protokolu používají základní Socket třídu k poskytování jednoduchého přístupu k síťovým službám bez režie na údržbu informací o stavu nebo znalost podrobností o nastavení soketů specifických pro protokol. Chcete-li použít asynchronní Socket metody, můžete použít asynchronní metody poskytované NetworkStream třídou. Chcete-li získat přístup k funkcím Socket třídy, které nejsou vystaveny třídami protokolu, je nutné použít Socket třídu.

TcpClient a TcpListener představuje síť pomocí NetworkStream třídy. Použijete metodu GetStream k vrácení síťového streamu a poté zavoláte metody NetworkStream.ReadAsync a NetworkStream.WriteAsync streamu. Základní NetworkStream soket třídy protokolu nevlastní, takže uzavření nemá vliv na soket.

Použijte TcpClient a TcpListener

Třída TcpClient požaduje data z internetového prostředku pomocí protokolu TCP. Metody a vlastnosti abstrahují TcpClient podrobnosti pro vytvoření Socket pro vyžádání a příjem dat pomocí protokolu TCP. Vzhledem k tomu, že připojení ke vzdálenému zařízení je reprezentováno jako datový proud, lze data číst a zapisovat pomocí technik zpracování datových proudů rozhraní .NET Framework.

Protokol TCP vytvoří připojení ke vzdálenému koncovému bodu a pak toto připojení použije k odesílání a přijímání datových paketů. TCP zodpovídá za zajištění, aby se datové pakety posílaly do koncového bodu a sestavovaly se ve správném pořadí, když dorazí.

Vytvoření koncového bodu PROTOKOLU IP

Při práci s System.Net.Socketsobjektem představujete koncový bod IPEndPoint sítě. IPEndPoint je zkonstruován za použití IPAddress a jeho odpovídajícího čísla portu. Než budete moct zahájit konverzaci prostřednictvím Socketaplikace, vytvoříte mezi aplikací a vzdáleným cílem datový kanál.

TCP/IP používá síťovou adresu a číslo portu služby k jednoznačné identifikaci služby. Síťová adresa identifikuje konkrétní cíl sítě; číslo portu identifikuje konkrétní službu na daném zařízení, ke které se má připojit. Kombinace síťové adresy a portu služby se nazývá koncový bod, který je reprezentován v .NET EndPoint třídou. Potomek EndPoint je definován pro každou podporovanou řadu adres; pro řadu IP adres je IPEndPointtřída .

Třída Dns poskytuje služby pro názvy domén aplikací, které používají internetové služby TCP/IP. Metoda GetHostEntryAsync se dotazuje serveru DNS na mapování uživatelsky přívětivého názvu domény (například "host.contoso.com") na číselnou internetovou adresu (například 192.168.1.1). GetHostEntryAsync Task<IPHostEntry> vrátí hodnotu, která při čekání obsahuje seznam adres a aliasů požadovaného názvu. Ve většině případů můžete použít první adresu v poli vrácenou AddressList. Následující kód načte IPAddress, který obsahuje IP adresu pro server host.contoso.com.

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

Návod

Pro účely ručního testování a ladění můžete obvykle použít metodu GetHostEntryAsync s výsledným názvem hostitele z Dns.GetHostName() hodnoty k překladu názvu místního hostitele na IP adresu. Vezměte v úvahu následující fragment kódu:

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

Autorita IANA (Internet Assigned Numbers Authority) definuje čísla portů pro běžné služby. Další informace najdete v IANA: registr názvů služeb a čísel portů přenosových protokolů). Ostatní služby můžou mít registrovaná čísla portů v rozsahu 1 024 až 65 535. Následující kód kombinuje IP adresu s host.contoso.com číslem portu a vytvoří vzdálený koncový bod pro připojení.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Po určení adresy vzdáleného zařízení a výběru portu, který se má pro připojení použít, může aplikace navázat připojení ke vzdálenému zařízení.

Vytvořte soubor TcpClient

Třída TcpClient poskytuje služby TCP na vyšší úrovni abstrakce než Socket třída. TcpClient slouží k vytvoření připojení klienta ke vzdálenému hostiteli. Když víte, jak získat IPEndPoint, předpokládejme, že máte IPAddress pro spárování s požadovaným číslem portu. Následující příklad ukazuje, jak nastavit TcpClient pro připojení k časovému serveru na TCP port 13.

var ipEndPoint = new IPEndPoint(ipAddress, 13);

using TcpClient client = new();
await client.ConnectAsync(ipEndPoint);
await using NetworkStream stream = client.GetStream();

var buffer = new byte[1_024];
int received = await stream.ReadAsync(buffer);

var message = Encoding.UTF8.GetString(buffer, 0, received);
Console.WriteLine($"Message received: \"{message}\"");
// Sample output:
//     Message received: "📅 8/22/2022 9:07:17 AM 🕛"

Předchozí kód jazyka C#:

  • Vytvoří IPEndPoint ze známého IPAddress a portu.
  • Vytvořte instanci nového TcpClient objektu.
  • Připojuje se client ke vzdálenému časovému serveru TCP na portu 13 pomocí TcpClient.ConnectAsync.
  • Používá NetworkStream ke čtení dat ze vzdáleného hostitele.
  • Deklaruje vyrovnávací paměť o velikosti 1_024 bajtů.
  • Čte data z stream do vyrovnávací paměti pro čtení.
  • Zapíše výsledky jako řetězec do konzoly.

Vzhledem k tomu, že klient ví, že je zpráva malá, může být celá zpráva načtena do vyrovnávací paměti pro čtení v jedné operaci. Při větších zprávách nebo zprávách s neurčitou délkou by měl klient použít vyrovnávací paměť vhodněji a číst ve smyčce while.

Důležité

Při odesílání a příjmu zpráv by mělo být předem známo jak pro server Encoding, tak pro klienta. Pokud například server komunikuje pomocí ASCIIEncoding , ale klient se pokusí použít UTF8Encoding, zprávy budou poškozeny.

Vytvořte soubor TcpListener

Tento TcpListener typ se používá k monitorování portu TCP pro příchozí požadavky a poté vytvoří buď Socket, nebo TcpClient, které spravuje připojení k klientovi. Metoda Start umožňuje naslouchání a Stop metoda zakáže naslouchání na portu. Metoda AcceptTcpClientAsync přijímá příchozí žádosti o připojení a vytvoří TcpClient požadavek pro zpracování a AcceptSocketAsync metoda přijímá příchozí požadavky na připojení a vytvoří Socket žádost o zpracování.

Následující příklad ukazuje vytvoření síťového časového serveru pomocí TcpListener k monitorování portu TCP 13. Když je přijat požadavek na příchozí připojení, časový server odpoví aktuálním datem a časem z hostitelského serveru.

var ipEndPoint = new IPEndPoint(IPAddress.Any, 13);
TcpListener listener = new(ipEndPoint);

try
{    
    listener.Start();

    using TcpClient handler = await listener.AcceptTcpClientAsync();
    await using NetworkStream stream = handler.GetStream();

    var message = $"📅 {DateTime.Now} 🕛";
    var dateTimeBytes = Encoding.UTF8.GetBytes(message);
    await stream.WriteAsync(dateTimeBytes);

    Console.WriteLine($"Sent message: \"{message}\"");
    // Sample output:
    //     Sent message: "📅 8/22/2022 9:07:17 AM 🕛"
}
finally
{
    listener.Stop();
}

Předchozí kód jazyka C#:

  • Vytvoří IPEndPoint s IPAddress.Any a portem.
  • Vytvořte instanci nového TcpListener objektu.
  • Volá metodu Start, která začne naslouchat na portu.
  • Používá TcpClient z metody AcceptTcpClientAsync ke schválení příchozích žádostí o připojení.
  • Zakóduje aktuální datum a čas jako řetězcovou zprávu.
  • Používá NetworkStream pro zápis dat do připojeného klienta.
  • Zapíše odeslanou zprávu do konzoly.
  • Nakonec zavolá metodu Stop , která zastaví naslouchání na portu.

Řízení TCP pro konečné s Socket třídou

Obě TcpClient i TcpListener interně spoléhají na třídu Socket, což znamená, že cokoli, co můžete dělat s těmito třídami, lze dosáhnout přímo pomocí soketů. Tato část ukazuje několik TcpClient a TcpListener případů použití, spolu s jejich Socket protějškem, který je funkčně ekvivalentní.

Vytvoření klientského soketu

TcpClientVýchozí konstruktor se pokusí vytvořit duální zásobníkový soket prostřednictvím konstruktoru Socket(SocketType, ProtocolType). Tento konstruktor vytvoří soket se dvěma zásobníky, pokud je podporován protokol IPv6, jinak se vrátí zpět na protokol IPv4.

Zvažte následující kód klienta TCP:

using var client = new TcpClient();

Předchozí kód klienta TCP je funkčně ekvivalentní následujícímu kódu soketu:

using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

Konstruktor TcpClient(AddressFamily)

Tento konstruktor přijímá jen tři AddressFamily hodnoty, jinak vyvolá ArgumentException. Platné hodnoty jsou:

Zvažte následující kód klienta TCP:

using var client = new TcpClient(AddressFamily.InterNetwork);

Předchozí kód klienta TCP je funkčně ekvivalentní následujícímu kódu soketu:

using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Konstruktor TcpClient(IPEndPoint)

Při vytváření soketu tento konstruktor také vytvoří vazbu k zadanému místnímuIPEndPoint. Vlastnost IPEndPoint.AddressFamily slouží k určení rodiny adres soketu.

Zvažte následující kód klienta TCP:

var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
using var client = new TcpClient(endPoint);

Předchozí kód klienta TCP je funkčně ekvivalentní následujícímu kódu soketu:

// Example IPEndPoint object
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);

Konstruktor TcpClient(String, Int32)

Tento konstruktor se pokusí vytvořit duální zásobník podobný výchozímu konstruktoru a připojit jej k vzdálenému koncovému bodu DNS definovanému dvojicí hostname a port.

Zvažte následující kód klienta TCP:

using var client = new TcpClient("www.example.com", 80);

Předchozí kód klienta TCP je funkčně ekvivalentní následujícímu kódu soketu:

using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);

Připojit k serveru

Všechna přetížení Connect, ConnectAsync, BeginConnect a EndConnect v TcpClient jsou funkčně ekvivalentní odpovídajícím Socket metodám.

Zvažte následující kód klienta TCP:

using var client = new TcpClient();
client.Connect("www.example.com", 80);

Výše uvedený TcpClient kód je ekvivalentní následujícímu kódu soketu:

using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);

Vytvoření serverového soketu

Podobně jako instance TcpClient, které mají funkční ekvivalenci se svými základními Socket protějšky, tato část mapuje TcpListener konstruktory na odpovídající socketový kód. Prvním konstruktorem, který je třeba vzít v úvahu, je TcpListener(IPAddress localaddr, int port).

var listener = new TcpListener(IPAddress.Loopback, 5000);

Předchozí kód naslouchacího procesu TCP je funkčně ekvivalentní následujícímu kódu soketu:

var ep = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

Zahájení naslouchání na serveru

Metoda Start() je obal, který kombinuje funkce Bind a Listen() z Socket.

Zvažte následující kód naslouchacího procesu TCP:

var listener = new TcpListener(IPAddress.Loopback, 5000);
listener.Start(10);

Předchozí kód naslouchacího procesu TCP je funkčně ekvivalentní následujícímu kódu soketu:

var endPoint = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);
try
{
    socket.Listen(10);
}
catch (SocketException)
{
    socket.Dispose();
}

Přijetí připojení k serveru

Pod kapotou příchozí připojení TCP vždy vytváří nový soket při přijetí. TcpListener může přijmout Socket instanci přímo (prostřednictvím AcceptSocket() nebo AcceptSocketAsync()) nebo může přijmout TcpClient (prostřednictvím AcceptTcpClient() a AcceptTcpClientAsync()).

Vezměte v úvahu následující TcpListener kód:

var listener = new TcpListener(IPAddress.Loopback, 5000);
using var acceptedSocket = await listener.AcceptSocketAsync();

// Synchronous alternative.
// var acceptedSocket = listener.AcceptSocket();

Předchozí kód naslouchacího procesu TCP je funkčně ekvivalentní následujícímu kódu soketu:

var endPoint = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
using var acceptedSocket = await socket.AcceptAsync();

// Synchronous alternative
// var acceptedSocket = socket.Accept();

Vytvoření zprávy NetworkStream pro odesílání a přijímání dat

S TcpClient potřebujete vytvořit instanci NetworkStream pomocí metody GetStream(), aby bylo možné odesílat a přijímat data. Musíte NetworkStream vytvořit ručně s použitím Socket.

Vezměte v úvahu následující TcpClient kód:

using var client = new TcpClient();
using NetworkStream stream = client.GetStream();

To je ekvivalentní následujícímu kódu soketu:

using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

// Be aware that transferring the ownership means that closing/disposing the stream will also close the underlying socket.
using var stream = new NetworkStream(socket, ownsSocket: true);

Návod

Pokud váš kód nepotřebuje pracovat s instancí Stream, můžete se místo vytvoření NetworkStream spolehnout přímo na metody Socket Send/Receive (Send, SendAsync, Receive a ReceiveAsync).

Viz také