Usar soquetes para enviar e receber dados por TCP
Antes de poder usar um soquete para se comunicar com dispositivos remotos, o soquete deve ser inicializado com as informações de protocolo e endereço de rede. O construtor da classe Socket tem parâmetros que especificam a família de endereços, o tipo de soquete e o tipo de protocolo usado pelo soquete para fazer conexões. Ao conectar um soquete de cliente a um soquete de servidor, o cliente usará um objeto IPEndPoint
para especificar o endereço de rede do servidor.
Criar um ponto de extremidade IP
Ao trabalhar com System.Net.Sockets, você representa um ponto de extremidade de rede como um objeto IPEndPoint. O IPEndPoint
é construído com um IPAddress e seu número de porta correspondente. Antes de iniciar uma conversa por meio de um Socket, crie um pipe de dados entre o aplicativo e o destino remoto.
O TCP/IP usa um endereço de rede e um número da porta de serviço para identificar um serviço exclusivamente. O endereço de rede identifica um destino de rede específico. O número da porta identifica o serviço específico nesse dispositivo ao qual se conectar. A combinação do endereço de rede e da porta de serviço é chamada de ponto de extremidade, que é representado no .NET pela classe EndPoint. Um descendente de EndPoint
está definido para cada família de endereços com suporte; para a família de endereços IP, a classe é IPEndPoint.
A classe Dns fornece serviços de nome de domínio para aplicativos que usam os serviços de Internet TCP/IP. O método GetHostEntryAsync consulta um servidor DNS para mapear um nome de domínio amigável (como “host.contoso.com”) para um endereço numérico na Internet (por exemplo, 192.168.1.1
). O GetHostEntryAsync
retorna um Task<IPHostEntry>
que é aguardando e contém uma lista de endereços e aliases para o nome solicitado. Na maioria dos casos, é possível usar o primeiro endereço retornado na matriz AddressList. O código a seguir obtém um IPAddress que contém o endereço IP do servidor host.contoso.com
.
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
Dica
Para fins de teste manual e depuração, normalmente você pode usar o método GetHostEntryAsync com o nome do host resultante do valor Dns.GetHostName() para resolver o nome do host local para um endereço IP. Considere o seguinte snippet de código:
var hostName = Dns.GetHostName();
IPHostEntry localhost = await Dns.GetHostEntryAsync(hostName);
// This is the IP address of the local machine
IPAddress localIpAddress = localhost.AddressList[0];
A IANA (Internet Assigned Numbers Authority) define os números de porta de serviços comuns. Para obter mais informações, confira IANA: registro de número da porta do protocolo de transporte e nome do serviço). Outros serviços podem ter números de porta registrados no intervalo de 1.024 a 65.535. O código a seguir combina o endereço IP de host.contoso.com
com um número da porta para criar um ponto de extremidade remoto para uma conexão.
IPEndPoint ipEndPoint = new(ipAddress, 11_000);
Depois de determinar o endereço do dispositivo remoto e escolher uma porta a ser usada para a conexão, o aplicativo pode estabelecer uma conexão com o dispositivo remoto.
Criar um cliente Socket
Com o objeto endPoint
criado, crie um soquete de cliente para se conectar ao servidor. Depois que o soquete estiver conectado, ele poderá enviar e receber dados da conexão de soquete do servidor.
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);
O código anterior do C#:
Cria uma instância de um novo objeto
Socket
com uma determinada família de endereços de instânciasendPoint
, o SocketType.Stream e o ProtocolType.Tcp.Chama o método Socket.ConnectAsync com a instância
endPoint
como um argumento.Em um loop
while
:- Codifica e envia uma mensagem para o servidor usando Socket.SendAsync.
- Grava a mensagem enviada no console.
- Inicializa um buffer para receber dados do servidor usando Socket.ReceiveAsync.
- Quando o
response
é uma confirmação, ele é gravado no console e o loop é encerrado.
Por fim, o soquete
client
chama Socket.Shutdown dado SocketShutdown.Both, que desliga as operações de envio e recebimento.
Criar um servidor Socket
Para criar o soquete do servidor, o objeto endPoint
pode escutar conexões de entrada em qualquer endereço IP, mas o número da porta deve ser especificado. Depois que o soquete é criado, o servidor pode aceitar conexões de entrada e se comunicar com clientes.
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|>"
}
O código anterior do C#:
Cria uma instância de um novo objeto
Socket
com uma determinada família de endereços de instânciasendPoint
, o SocketType.Stream e o ProtocolType.Tcp.O
listener
chama o método Socket.Bind com a instânciaendPoint
como um argumento para associar o soquete ao endereço de rede.O método Socket.Listen() é chamado para escutar conexões de entrada.
O
listener
chama o método Socket.AcceptAsync para aceitar uma conexão de entrada no soquetehandler
.Em um loop
while
:- Chama Socket.ReceiveAsync para receber dados do cliente.
- Quando os dados são recebidos, eles são decodificados e gravados no console.
- Se a mensagem
response
terminar com<|EOM|>
, uma confirmação será enviada ao cliente usando o Socket.SendAsync.
Executar o cliente e o servidor de exemplo
Inicie o aplicativo de servidor primeiro e, em seguida, o aplicativo cliente.
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...
O aplicativo cliente enviará uma mensagem ao servidor e o servidor responderá com uma confirmação.
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...