Delen via


TCP-overzicht

Belangrijk

De Socket klasse wordt ten zeerste aanbevolen voor geavanceerde gebruikers, in plaats van TcpClient en TcpListener.

Als u wilt werken met Transmission Control Protocol (TCP), hebt u twee opties: voor Socket maximale controle en prestaties, of gebruik de TcpClient en TcpListener helperklassen. TcpClient en TcpListener zijn gebouwd op basis van de System.Net.Sockets.Socket klasse en zorgen voor de details van het overdragen van gegevens voor gebruiksgemak.

De protocolklassen gebruiken de onderliggende Socket klasse om eenvoudige toegang tot netwerkservices te bieden zonder de overhead van het onderhouden van statusinformatie of het kennen van de details van het instellen van protocolspecifieke sockets. Als u asynchrone Socket methoden wilt gebruiken, kunt u de asynchrone methoden van de NetworkStream klasse gebruiken. Als u toegang wilt krijgen tot functies van de Socket klasse die niet worden weergegeven door de protocolklassen, moet u de Socket klasse gebruiken.

TcpClient en TcpListener vertegenwoordig het netwerk met behulp van de NetworkStream klasse. U gebruikt de GetStream methode om de netwerkstroom te retourneren en roept vervolgens de stream NetworkStream.ReadAsync en NetworkStream.WriteAsync methoden aan. De NetworkStream socket van de protocolklassen is niet eigenaar van de onderliggende socket van de protocolklasse, dus het sluiten heeft geen invloed op de socket.

Gebruiken TcpClient en TcpListener

De TcpClient klasse vraagt gegevens van een internetresource aan met behulp van TCP. De methoden en eigenschappen van TcpClient abstracte details voor het maken van een Socket voor het aanvragen en ontvangen van gegevens met behulp van TCP. Omdat de verbinding met het externe apparaat wordt weergegeven als een stroom, kunnen gegevens worden gelezen en geschreven met technieken voor de verwerking van .NET Framework-stromen.

Het TCP-protocol brengt een verbinding tot stand met een extern eindpunt en gebruikt vervolgens die verbinding om gegevenspakketten te verzenden en te ontvangen. TCP is verantwoordelijk voor het garanderen dat gegevenspakketten naar het eindpunt worden verzonden en in de juiste volgorde worden samengesteld wanneer ze binnenkomen.

Een IP-eindpunt maken

Wanneer u werkt System.Net.Sockets, vertegenwoordigt u een netwerkeindpunt als een IPEndPoint object. Het IPEndPoint is samengesteld met een IPAddress en het bijbehorende poortnummer. Voordat u een gesprek kunt starten via een Socket, maakt u een gegevenspijp tussen uw app en de externe bestemming.

TCP/IP maakt gebruik van een netwerkadres en een servicepoortnummer om een service uniek te identificeren. Het netwerkadres identificeert een specifieke netwerkbestemming; het poortnummer identificeert de specifieke service op dat apparaat waarmee verbinding moet worden gemaakt. De combinatie van netwerkadres en servicepoort wordt een eindpunt genoemd, dat wordt weergegeven in .NET door de EndPoint klasse. Er wordt een afstammeling van EndPoint gedefinieerd voor elke ondersteunde adresfamilie; voor de IP-adresfamilie is IPEndPointde klasse.

De Dns klasse biedt domeinnaamservices voor apps die gebruikmaken van TCP/IP-internetservices. De GetHostEntryAsync methode voert een query uit op een DNS-server om een gebruiksvriendelijke domeinnaam (zoals 'host.contoso.com') toe te wijzen aan een numeriek internetadres (zoals 192.168.1.1). GetHostEntryAsync retourneert een Task<IPHostEntry> lijst met adressen en aliassen voor de aangevraagde naam wanneer dit wordt verwacht. In de meeste gevallen kunt u het eerste adres gebruiken dat in de AddressList matrix wordt geretourneerd. Met de volgende code wordt een IPAddress met het IP-adres voor de server host.contoso.comophaalt.

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

Tip

Voor handmatige tests en foutopsporing kunt u doorgaans de GetHostEntryAsync methode gebruiken met de resulterende hostnaam van de Dns.GetHostName() waarde om de localhost-naam om te zetten in een IP-adres. Bekijk het volgende codefragment:

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

De Internet Assigned Numbers Authority (IANA) definieert poortnummers voor algemene services. Zie IANA: Service Name and Transport Protocol Port Number Registry) voor meer informatie. Andere services kunnen poortnummers hebben geregistreerd in het bereik van 1024 tot 65.535. De volgende code combineert het IP-adres voor host.contoso.com met een poortnummer om een extern eindpunt voor een verbinding te maken.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

Na het bepalen van het adres van het externe apparaat en het kiezen van een poort die moet worden gebruikt voor de verbinding, kan de app verbinding maken met het externe apparaat.

Maak een TcpClient

De TcpClient klasse biedt TCP-services op een hoger abstractieniveau dan de Socket klasse. TcpClient wordt gebruikt om een clientverbinding met een externe host te maken. Als u weet hoe u een IPEndPointnummer krijgt, gaan we ervan uit dat u een IPAddress moet koppelen aan uw gewenste poortnummer. In het volgende voorbeeld ziet u hoe u een TcpClient verbinding kunt maken met een tijdserver op TCP-poort 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 🕛"

De voorgaande C#-code:

  • Hiermee maakt u een IPEndPoint van een bekende IPAddress en poort.
  • Instantieer een nieuw TcpClient object.
  • Hiermee maakt u verbinding client met de externe TCP-tijdserver op poort 13 met behulp van TcpClient.ConnectAsync.
  • NetworkStream Hiermee kunt u gegevens van de externe host lezen.
  • Declareert een leesbuffer van 1_024 bytes.
  • Leest gegevens uit de stream in de leesbuffer.
  • Hiermee schrijft u de resultaten als een tekenreeks naar de console.

Omdat de client weet dat het bericht klein is, kan het hele bericht in ÊÊn bewerking in de leesbuffer worden gelezen. Bij grotere berichten of berichten met een onbepaalde lengte moet de client de buffer beter gebruiken en in een while lus lezen.

Belangrijk

Wanneer u berichten verzendt en ontvangt, moet u Encoding van tevoren bekend zijn bij zowel de server als de client. Als de server bijvoorbeeld communiceert met ASCIIEncoding behulp van maar de client probeert te gebruiken UTF8Encoding, worden de berichten onjuist ingedeeld.

Maak een TcpListener

Het TcpListener type wordt gebruikt om een TCP-poort te bewaken voor binnenkomende aanvragen en vervolgens een Socket of een TcpClient poort te maken waarmee de verbinding met de client wordt beheerd. De Start methode schakelt luisteren in en de Stop methode schakelt luisteren op de poort uit. De AcceptTcpClientAsync methode accepteert binnenkomende verbindingsaanvragen en maakt een TcpClient om de aanvraag te verwerken. De AcceptSocketAsync methode accepteert binnenkomende verbindingsaanvragen en maakt een Socket om de aanvraag te verwerken.

In het volgende voorbeeld ziet u hoe u een netwerktijdserver maakt met behulp van een TcpListener server voor het bewaken van TCP-poort 13. Wanneer een binnenkomende verbindingsaanvraag wordt geaccepteerd, reageert de tijdserver met de huidige datum en tijd van de hostserver.

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

De voorgaande C#-code:

  • Hiermee maakt u een IPEndPoint met IPAddress.Any en poort.
  • Instantieer een nieuw TcpListener object.
  • Roept de Start methode aan om te beginnen met luisteren op de poort.
  • Hiermee gebruikt u een TcpClient van de AcceptTcpClientAsync methode om binnenkomende verbindingsaanvragen te accepteren.
  • Codeert de huidige datum en tijd als een tekenreeksbericht.
  • NetworkStream Hiermee schrijft u gegevens naar de verbonden client.
  • Hiermee schrijft u het verzonden bericht naar de console.
  • Ten slotte roept u de Stop methode aan om te stoppen met luisteren op de poort.

Eindig TCP-besturingselement met de Socket klasse

Zowel TcpClient als TcpListener intern vertrouwen op de Socket klasse, wat betekent dat alles wat u met deze klassen kunt doen, rechtstreeks met sockets kan worden bereikt. In deze sectie ziet u verschillende TcpClient en TcpListener use cases, samen met hun Socket tegenhanger die functioneel gelijkwaardig is.

Een clientsocket maken

TcpClientDe standaardconstructor probeert een dual-stack socket te maken via de socket-constructor (SocketType, ProtocolType). Deze constructor maakt een dual-stack socket als IPv6 wordt ondersteund, anders valt deze terug op IPv4.

Houd rekening met de volgende TCP-clientcode:

using var client = new TcpClient();

De voorgaande TCP-clientcode is functioneel gelijk aan de volgende socketcode:

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

De TcpClient(AddressFamily) constructor

Deze constructor accepteert slechts drie AddressFamily waarden, anders wordt er een ArgumentException. Geldige waarden zijn:

Houd rekening met de volgende TCP-clientcode:

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

De voorgaande TCP-clientcode is functioneel gelijk aan de volgende socketcode:

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

De TcpClient(IPEndPoint) constructor

Bij het maken van de socket wordt deze constructor ook verbonden met de opgegeven lokaleIPEndPoint. De IPEndPoint.AddressFamily eigenschap wordt gebruikt om de adresfamilie van de socket te bepalen.

Houd rekening met de volgende TCP-clientcode:

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

De voorgaande TCP-clientcode is functioneel gelijk aan de volgende socketcode:

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

De TcpClient(String, Int32) constructor

Deze constructor probeert een dual-stack te maken die vergelijkbaar is met de standaardconstructor en deze te verbinden met het externe DNS-eindpunt dat is gedefinieerd door het hostname en port paar.

Houd rekening met de volgende TCP-clientcode:

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

De voorgaande TCP-clientcode is functioneel gelijk aan de volgende socketcode:

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

Met server verbinden

Alle Connect, ConnectAsyncBeginConnect en EndConnect overbelastingen in TcpClient zijn functioneel gelijk aan de bijbehorende Socket methoden.

Houd rekening met de volgende TCP-clientcode:

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

De bovenstaande TcpClient code is gelijk aan de volgende socketcode:

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

Een serversocket maken

Net zoals TcpClient exemplaren met functionele gelijkwaardigheid met hun onbewerkte Socket tegenhangers, worden TcpListener in deze sectie constructors toegewezen aan de bijbehorende socketcode. De eerste constructor die u moet overwegen, is de TcpListener(IPAddress localaddr, int port).

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

De voorgaande TCP-listenercode is functioneel gelijk aan de volgende socketcode:

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

Beginnen met luisteren op de server

De Start() methode is een wrapper die de functies Bind en Listen() functionaliteit combineertSocket.

Houd rekening met de volgende TCP-listenercode:

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

De voorgaande TCP-listenercode is functioneel gelijk aan de volgende socketcode:

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

Een serververbinding accepteren

Onder de schermen worden binnenkomende TCP-verbindingen altijd een nieuwe socket gemaakt wanneer deze wordt geaccepteerd. TcpListener kan een Socket exemplaar rechtstreeks (via AcceptSocket() of AcceptSocketAsync()) accepteren of een TcpClient (via AcceptTcpClient() en AcceptTcpClientAsync()) accepteren.

Houd rekening met de volgende TcpListener code:

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

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

De voorgaande TCP-listenercode is functioneel gelijk aan de volgende socketcode:

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

NetworkStream Een gegevens verzenden en ontvangen maken

U TcpClient moet een NetworkStream instantie maken met de GetStream() methode om gegevens te kunnen verzenden en ontvangen. Met Socket, moet u het NetworkStream maken handmatig.

Houd rekening met de volgende TcpClient code:

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

Dit komt overeen met de volgende socketcode:

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

Als uw code niet met een Stream exemplaar hoeft te werken, kunt u rechtstreeks vertrouwen op Socketde methoden voor verzenden/ontvangen (Send, SendAsyncReceive en ReceiveAsync) in plaats van een NetworkStream.

Zie ook