Границы сообщения TCP в C#
TCP передаёт поток байтов: один вызов
WriteAsync на стороне отправителя не
обязан совпасть с одним ReadAsync у
получателя. Поэтому приложение само задаёт
рамку сообщения - часто символ перевода
строки \n в конце каждой логической фразы.
Построчный протокол: отправитель дописывает
\n, получатель накапливает байты в буфер
и режет строки по разделителю. Ниже сервер читает
одну строку "ping" и отвечает
"pong\n"; клиент печатает ответ без
символа перевода строки:
using System.Net;
using System.Net.Sockets;
using System.Text;
Task server = Task.Run(async () =>
{
TcpListener listener = new(IPAddress.Loopback, 18080);
listener.Start();
using TcpClient peer = await listener.AcceptTcpClientAsync();
using NetworkStream stream = peer.GetStream();
byte[] buf = new byte[256];
int n = await stream.ReadAsync(buf);
string line = Encoding.UTF8.GetString(buf, 0, n).TrimEnd('\n');
if (line == "ping")
{
await stream.WriteAsync(Encoding.UTF8.GetBytes("pong\n"));
}
listener.Stop();
});
await Task.Delay(30);
using TcpClient client = new();
await client.ConnectAsync("127.0.0.1", 18080);
using NetworkStream stream = client.GetStream();
await stream.WriteAsync(Encoding.UTF8.GetBytes("ping\n"));
byte[] buf = new byte[256];
int got = await stream.ReadAsync(buf);
Console.WriteLine(Encoding.UTF8.GetString(buf, 0, got).TrimEnd());
await server;
Два WriteAsync подряд могут прийти одним
ReadAsync - тогда строки режут через
Split('\n') по фактически прочитанной
длине. Так клиент отправляет "one\n" и
"two\n" двумя вызовами и закрывает отправку
через Shutdown. Если обе фразы пришли одним
куском, сервер находит их по разделителю:
using System.Net;
using System.Net.Sockets;
using System.Text;
Task server = Task.Run(async () =>
{
TcpListener listener = new(IPAddress.Loopback, 18080);
listener.Start();
using TcpClient peer = await listener.AcceptTcpClientAsync();
using NetworkStream stream = peer.GetStream();
byte[] buf = new byte[256];
int n = await stream.ReadAsync(buf);
foreach (string part in Encoding.UTF8.GetString(buf, 0, n).Split(
'\n', StringSplitOptions.RemoveEmptyEntries))
{
Console.WriteLine(part);
}
listener.Stop();
});
await Task.Delay(30);
using TcpClient client = new();
await client.ConnectAsync("127.0.0.1", 18080);
using NetworkStream stream = client.GetStream();
await stream.WriteAsync(Encoding.UTF8.GetBytes("one\n"));
await stream.WriteAsync(Encoding.UTF8.GetBytes("two\n"));
client.Client.Shutdown(SocketShutdown.Send);
await server;
Отправьте строку "ask\n", получите ответ
"reply: ask\n" и выведите его в консоли
клиента одной строкой.