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ď pro Socket maximální kontrolu a výkon, nebo použijte TcpClient pomocné třídy.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. Tuto metodu GetStream použijete k vrácení síťového streamu a následnému volání streamu NetworkStream.ReadAsync a NetworkStream.WriteAsync metod. Základní NetworkStream soket třídy protokolu nevlastní, takže uzavření nemá vliv na soket.

Použití 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ě. Vytvoří IPEndPoint se s odpovídajícím číslem portu a jeho odpovídajícím číslem IPAddress 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). GetHostEntryAsyncTask<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 vrácenou AddressList v poli. Následující kód získá IPAddress obsahující IP adresu serveru host.contoso.com.

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

Tip

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 tématu IANA: Název služby a registr čísel portů přenosového protokolu). 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ž budete vědět, jak získat , IPEndPointpředpokládejme, že máte IPAddress párování s požadovaným číslem portu. Následující příklad ukazuje nastavení TcpClient připojení k časovému serveru na portu TCP 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ří ze IPEndPoint známého IPAddress portu a portu.
  • Vytvořte instanci nového TcpClient objektu.
  • client Připojení vzdálený časový server TCP na portu 13 pomocí TcpClient.ConnectAsync.
  • NetworkStream Používá ke čtení dat ze vzdáleného hostitele.
  • Deklaruje vyrovnávací paměť 1_024 pro čtení bajtů.
  • Čte data z stream 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. U větších zpráv nebo zpráv s nedeterminovat délkou by měl klient použít vyrovnávací paměť vhodnější 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 pak vytvoří Socket buď port TCP, nebo který TcpClient 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í monitoru TcpListener 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ří s příponou IPEndPointIPAddress.Any a portem.
  • Vytvořte instanci nového TcpListener objektu.
  • Zavolá metodu, Start která začne naslouchat na portu.
  • Používá metodu TcpClientAcceptTcpClientAsync pro příjem příchozích požadavků na připojení.
  • Zakóduje aktuální datum a čas jako řetězcovou zprávu.
  • Používá k zápisu NetworkStream dat do připojeného klienta.
  • Zapíše odeslanou zprávu do konzoly.
  • Nakonec zavolá metodu Stop , která zastaví naslouchání na portu.

Finite TCP control with the Socket class

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

Vytvoření klientského soketu

TcpClientVýchozí konstruktor se pokusí vytvořit soket s duálním zásobníkem 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á pouze tři AddressFamily hodnoty, jinak vyvolá výjimku 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 bude tento konstruktor také svázat se zadaným místnímIPEndPoint prostředím. 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řipojí ho ke vzdálenému koncovému bodu DNS definovanému parametrem a port páremhostname.

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šechny Connect, ConnectAsyncBeginConnect a EndConnect přetížení v TcpClient jsou funkčně ekvivalentní odpovídající Socket metody.

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 TcpClient instance, které mají funkční ekvivalenci se svými nezpracovanými Socket protějšky, tato část mapuje TcpListener konstruktory na odpovídající kód soketu. Prvním konstruktorem, který je třeba vzít v TcpListener(IPAddress localaddr, int port)úvahu, je .

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 obálka kombinující Socket's Bind a Listen() funkce.

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 s metodou NetworkStreamGetStream() , aby bylo možné odesílat a přijímat data . V Socketpřípadě, že musíte vytvoření provést NetworkStream ručně.

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

Tip

Pokud váš kód nepotřebuje pracovat s Stream instancí, můžete místo vytvoření NetworkStreamspoléhat přímo na Socketmetody Send/Receive (ReceiveSendSendAsyncaReceiveAsync) metody Send/Receive .

Viz také