A TCP áttekintése
Fontos
Az Socket osztály kifejezetten ajánlott a haladó felhasználók számára ahelyett, TcpClient
hogy és TcpListener
.
A Transmission Control Protocol (TCP) használatához két lehetősége van: vagy a maximális vezérléshez és a teljesítményhez, Socket vagy használja a TcpClient TcpListener segédosztályokat. TcpClient és TcpListener az osztályra System.Net.Sockets.Socket épülnek, és a könnyű használat érdekében gondoskodnak az adatok átvitelének részleteiről.
A protokollosztályok az alapul szolgáló Socket
osztály használatával egyszerű hozzáférést biztosítanak a hálózati szolgáltatásokhoz az állapotinformációk fenntartása vagy a protokollspecifikus szoftvercsatornák beállításának részleteinek ismerete nélkül. Az aszinkron Socket
metódusok használatához használhatja az osztály által NetworkStream biztosított aszinkron metódusokat. A protokollosztályok által nem közzétett osztály funkcióinak Socket
eléréséhez az osztályt Socket
kell használnia.
TcpClient
és TcpListener
az osztály használatával képviseli a NetworkStream
hálózatot. A metódussal GetStream visszaadhatja a hálózati streamet, majd meghívhatja a streamet NetworkStream.ReadAsync és NetworkStream.WriteAsync a metódusokat. A NetworkStream
protokollosztályok alapjául szolgáló szoftvercsatornának nincs tulajdonosa, ezért a lezárás nem befolyásolja a szoftvercsatornát.
Használat TcpClient
és TcpListener
Az TcpClient osztály TCP használatával kér adatokat egy internetes erőforrástól. Az absztrakció módszerei és tulajdonságai TcpClient
a TCP használatával történő adatkéréshez és -fogadáshoz szükséges létrehozás részletei Socket . Mivel a távoli eszközhöz való kapcsolat streamként van ábrázolva, az adatok .NET-keretrendszer streamkezelési technikákkal olvashatók és írhatók.
A TCP protokoll kapcsolatot létesít egy távoli végponttal, majd ezt a kapcsolatot használja adatcsomagok küldéséhez és fogadásához. A TCP feladata annak biztosítása, hogy az adatcsomagok a végpontra érkezve a megfelelő sorrendben legyenek összeállítva.
IP-végpont létrehozása
A munka System.Net.Socketssorán egy hálózati végpontot jelöl objektumként IPEndPoint . A IPEndPoint
felépítése egy IPAddress és annak megfelelő portszámmal történik. Mielőtt beszélgetést kezdeményezhet egy Socketalkalmazáson keresztül, hozzon létre egy adatcsatornát az alkalmazás és a távoli cél között.
A TCP/IP egy hálózati címet és egy szolgáltatásportszámot használ a szolgáltatás egyedi azonosításához. A hálózati cím azonosít egy adott hálózati célt; a portszám azonosítja az adott szolgáltatást azon az eszközön, amelyhez csatlakozni szeretne. A hálózati cím és a szolgáltatásport kombinációját végpontnak nevezzük, amelyet a .NET-ben az EndPoint osztály jelöl. A program minden támogatott címcsaládhoz meghatároz egy leszármazottat EndPoint
; az IP-címcsalád esetében az osztály a IPEndPoint.
Az Dns osztály tartománynév-szolgáltatásokat biztosít a TCP/IP internetszolgáltatásokat használó alkalmazások számára. A GetHostEntryAsync metódus lekérdez egy DNS-kiszolgálót egy felhasználóbarát tartománynév (például "host.contoso.com") numerikus internetcímre (például 192.168.1.1
). GetHostEntryAsync
visszaad egy Task<IPHostEntry>
olyan értéket, amely a várt állapotban tartalmazza a kért név címeinek és aliasainak listáját. A legtöbb esetben használhatja a tömbben AddressList visszaadott első címet. A következő kód a IPAddress kiszolgáló host.contoso.com
IP-címét tartalmazza.
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
Tipp.
Manuális tesztelési és hibakeresési célokra általában a GetHostEntryAsync metódust használhatja az eredményül kapott gazdagépnévvel az Dns.GetHostName() értékből a localhost név IP-címre való feloldásához. Vegye figyelembe a következő kódrészletet:
var hostName = Dns.GetHostName();
IPHostEntry localhost = await Dns.GetHostEntryAsync(hostName);
// This is the IP address of the local machine
IPAddress localIpAddress = localhost.AddressList[0];
Az Internet Assigned Numbers Authority (IANA) a gyakori szolgáltatások portszámait határozza meg. További információ : IANA: Service Name and Transport Protocol Port Number Registry). Más szolgáltatások 1024 és 65 535 közötti tartományba tartozó regisztrált portszámokkal rendelkezhetnek. Az alábbi kód egy portszámmal egyesíti az IP-címet host.contoso.com
egy távoli végpont létrehozásához egy kapcsolathoz.
IPEndPoint ipEndPoint = new(ipAddress, 11_000);
A távoli eszköz címének meghatározása és a kapcsolathoz használandó port kiválasztása után az alkalmazás kapcsolatot létesíthet a távoli eszközzel.
Hozzon létre egy TcpClient
Az TcpClient
osztály a TCP-szolgáltatásokat az osztálynál Socket
magasabb absztrakciós szinten biztosítja. TcpClient
egy távoli gazdagép ügyfélkapcsolatának létrehozására szolgál. Tudva, hogyan szerezhet be egy IPEndPoint
, tegyük fel, hogy van egy IPAddress
párosítani a kívánt port számát. Az alábbi példa egy időkiszolgálóhoz való csatlakozás beállítását TcpClient
mutatja be a 13-os TCP-porton:
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 🕛"
Az előző C# kód:
- Egy ismert
IPAddress
és egy portból hoz létre.IPEndPoint
- Új
TcpClient
objektum példányosítása. - Csatlakoztatja a
client
távoli TCP-időkiszolgálót a 13-as porton a használatával TcpClient.ConnectAsync. - NetworkStream A távoli gazdagépről származó adatok olvasására szolgál.
- Bájtok olvasási pufferét
1_024
deklarálja. - Beolvassa az adatokat az olvasási
stream
pufferbe. - Az eredményeket sztringként írja a konzolra.
Mivel az ügyfél tudja, hogy az üzenet kicsi, a teljes üzenet beolvasható az olvasási pufferbe egyetlen műveletben. Nagyobb üzenetek vagy meghatározhatatlan hosszúságú üzenetek esetén az ügyfélnek a puffert megfelelőbben kell használnia, és egy while
hurokban kell olvasnia.
Fontos
Üzenetek küldésekor és fogadásakor a kiszolgálónak és az Encoding ügyfélnek is előre tudnia kell. Ha például a kiszolgáló a használatával ASCIIEncoding kommunikál, de az ügyfél megpróbálja használni UTF8Encoding, az üzenetek helytelenül lesznek formázva.
Hozzon létre egy TcpListener
Ez TcpListener a típus a bejövő kérések TCP-portjának figyelésére szolgál, majd létrehoz egy Socket
vagy egy TcpClient
, az ügyfélhez való kapcsolatot kezelőt. A Start metódus lehetővé teszi a figyelés használatát, a Stop metódus pedig letiltja a port hallgatását. A AcceptTcpClientAsync metódus fogadja a bejövő kapcsolatkéréseket, és létrehoz egy TcpClient
kérést a kérés kezeléséhez, a metódus pedig fogadja a AcceptSocketAsync bejövő kapcsolatkéréseket, és létrehoz egy Socket
kérést a kérés kezeléséhez.
Az alábbi példa bemutatja, hogyan hozhat létre hálózati időkiszolgálót a TcpListener
13-ás TCP-port figyeléséhez. Bejövő kapcsolatkérés elfogadásakor az időkiszolgáló az aktuális dátummal és időponttal válaszol a gazdakiszolgálótól.
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();
}
Az előző C# kód:
- Létrehoz egy
IPEndPoint
with IPAddress.Any and portot. - Új
TcpListener
objektum példányosítása. - Meghívja a metódust Start , hogy elkezdjen figyelni a porton.
- A metódus egyikével
TcpClient
fogadja a AcceptTcpClientAsync bejövő kapcsolatkéréseket. - Az aktuális dátumot és időt sztringüzenetként kódolja.
- Az a NetworkStream használatával adatokat írhat a csatlakoztatott ügyfélnek.
- Az elküldött üzenetet a konzolra írja.
- Végül meghívja a metódust Stop , hogy ne hallgassa meg a portot.
Véges TCP-vezérlő az Socket
osztálysal
Mind belsőlegTcpListener
, mind TcpClient
az Socket
osztályra támaszkodik, ami azt jelenti, hogy minden, amit ezekkel az osztályokkal el lehet érni közvetlenül szoftvercsatornákkal. Ez a szakasz számos TcpClient
használati TcpListener
esetet mutat be, valamint Socket
azok funkcionálisan egyenértékű megfelelőjét.
Ügyfélcsatorna létrehozása
TcpClient
Az alapértelmezett konstruktor kettős veremű szoftvercsatornát próbál létrehozni a Socket(SocketType, ProtocolType) konstruktoron keresztül. Ez a konstruktor kettős veremű szoftvercsatornát hoz létre, ha az IPv6 támogatott, ellenkező esetben az IPv4-be kerül vissza.
Vegye figyelembe a következő TCP-ügyfélkódot:
using var client = new TcpClient();
Az előző TCP-ügyfélkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
A TcpClient(AddressFamily) konstruktor
Ez a konstruktor csak három AddressFamily
értéket fogad el, ellenkező esetben egy ArgumentException. Az érvényes értékek a következők:
- AddressFamily.InterNetwork: IPv4-szoftvercsatornához.
- AddressFamily.InterNetworkV6: IPv6-szoftvercsatornához.
- AddressFamily.Unknown: ez egy kettős veremű szoftvercsatornát próbál létrehozni, hasonlóan az alapértelmezett konstruktorhoz.
Vegye figyelembe a következő TCP-ügyfélkódot:
using var client = new TcpClient(AddressFamily.InterNetwork);
Az előző TCP-ügyfélkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
A TcpClient(IPEndPoint) konstruktor
A szoftvercsatorna létrehozásakor a konstruktor a megadott helyihez IPEndPoint
is kötődik. A IPEndPoint.AddressFamily tulajdonság a szoftvercsatorna címcsaládjának meghatározására szolgál.
Vegye figyelembe a következő TCP-ügyfélkódot:
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
using var client = new TcpClient(endPoint);
Az előző TCP-ügyfélkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
// 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);
A TcpClient(String, Int32) konstruktor
Ez a konstruktor megkísérli létrehozni az alapértelmezett konstruktorhoz hasonló kettős vermet, és csatlakoztatni az és port
a pár által hostname
meghatározott távoli DNS-végponthoz.
Vegye figyelembe a következő TCP-ügyfélkódot:
using var client = new TcpClient("www.example.com", 80);
Az előző TCP-ügyfélkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);
Csatlakozás kiszolgálóhoz
Az összes Connect
, BeginConnect
ConnectAsync
és EndConnect
a TcpClient
túlterhelések funkcionálisan egyenértékűek a megfelelő Socket
módszerekkel.
Vegye figyelembe a következő TCP-ügyfélkódot:
using var client = new TcpClient();
client.Connect("www.example.com", 80);
A fenti TcpClient
kód egyenértékű a következő szoftvercsatorna-kóddal:
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect("www.example.com", 80);
Kiszolgálói szoftvercsatorna létrehozása
A nyers Socket
megfelelőkkel funkcionális egyenértékűséggel rendelkező példányokhoz hasonlóan TcpClient
ez a szakasz a konstruktorokat a megfelelő szoftvercsatorna-kódhoz rendeliTcpListener
. Az első konstruktor, amelyet figyelembe kell venni, a TcpListener(IPAddress localaddr, int port)
.
var listener = new TcpListener(IPAddress.Loopback, 5000);
Az előző TCP-figyelőkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
var ep = new IPEndPoint(IPAddress.Loopback, 5000);
using var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Figyelés indítása a kiszolgálón
A Start() metódus egy burkoló, amely egyesíti az 's Bind és Listen() a Socket
funkcionalitás.
Vegye figyelembe a következő TCP-figyelőkódot:
var listener = new TcpListener(IPAddress.Loopback, 5000);
listener.Start(10);
Az előző TCP-figyelőkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
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();
}
Kiszolgálókapcsolat elfogadása
A kapucni alatt a bejövő TCP-kapcsolatok mindig új szoftvercsatornát hoznak létre, ha elfogadják. TcpListener
közvetlenül (keresztül vagy) elfogadhat egy Socket példányt, vagy elfogadhat egy (keresztül ésAcceptTcpClientAsync()) példányt TcpClient AcceptTcpClient().AcceptSocket() AcceptSocketAsync()
Vegye figyelembe a következő TcpListener
kódot:
var listener = new TcpListener(IPAddress.Loopback, 5000);
using var acceptedSocket = await listener.AcceptSocketAsync();
// Synchronous alternative.
// var acceptedSocket = listener.AcceptSocket();
Az előző TCP-figyelőkód funkcionálisan egyenértékű a következő szoftvercsatornás kóddal:
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
Adatok küldésére és fogadására vonatkozó létrehozás
Az TcpClient
adatok küldéséhez és fogadásához a metódussal GetStream() kell példányt NetworkStream létrehoznia. Ezzel a lépéssel Socket
manuálisan kell létrehoznia a létrehozást NetworkStream
.
Vegye figyelembe a következő TcpClient
kódot:
using var client = new TcpClient();
using NetworkStream stream = client.GetStream();
Ami egyenértékű a következő szoftvercsatorna-kóddal:
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);
Tipp.
Ha a kódnak nem kell egy példánysal Stream dolgoznia, közvetlenül a küldési/fogadási metódusokra (Sendés ReceiveAsync) támaszkodhat Socket
ahelyett, SendAsyncReceive hogy létrehoz egy példánytNetworkStream.