Клиент-серверное взаимодействие по протоколу TCP в C#
Здравствуйте начал изучать C# и застрял на теме клиент-серверное взаимодействие.
Вот код:
'''
const string ip = "127.0.0.1"; int port = 8080;
var tcpEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
var tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSocket.Bind(tcpEndPoint);
tcpSocket.Listen(5);
while (true)
{
var listener = tcpSocket.Accept();
var byfer = new byte[256];
var size = 0;
var data = new StringBuilder();
do
{
size = listener.Receive(byfer);
data.Append(Encoding.UTF8.GetString(byfer, 0, size));
} while (listener.Available > 0);
Console.WriteLine(data);
listener.Send(Encoding.UTF8.GetBytes("Успех"));
listener.Shutdown(SocketShutdown.Both);
listener.Close();
''' Помогите разобраться за что отвечает каждая строка данного кода и как все это работает. Заранее благодарю за помощь!!!
Ответы (2 шт):
//задаем переменную, которая хранит айпишник и порт объекта, который будем слушать
var tcpEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
//задаем переменную, которая хранит инфу о сокете
var tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Связывает объект Socket с конечной точкой
tcpSocket.Bind(tcpEndPoint);
//Устанавливает объект Socket в состояние прослушивания
tcpSocket.Listen(5);
while (true)
{
//Извлекает из очереди запрос на подкючение и реализует его
var listener = tcpSocket.Accept();
var byfer = new byte[256];
var size = 0;
var data = new StringBuilder();
do
{
//Cчитывает данные из byfer и возвращает число успешно считанных байтов
size = listener.Receive(byfer);
data.Append(Encoding.UTF8.GetString(byfer, 0, size));
} while (listener.Available > 0);
Console.WriteLine(data);
//Отправляет на сокет сообщение (которое может быть отображено на странице браузера например, если мы сделает Httplistner)
listener.Send(Encoding.UTF8.GetBytes("Успех"));
//Закрывает сокет
listener.Shutdown(SocketShutdown.Both);
//Вырубаем прослушку (закрываем листнер)
listener.Close();
Как и всё в программировании, очень просто, но сперва не понятно.
Как работают сокеты? Можете представить свой компьютер как помпу, а сокеты – отверстия в этой помпе. У каждой помпы есть свой адрес – IP, к каждого сокета есть свой id – адрес помпы + порт. Зачем нужны сокеты? Для создания каналов. Если клиент хочет послать сообщение cерверу, например, Hello, World!, то он сперва должен создать канал. Он спамит запрос о создании канала на серверный сокет, серверный сокет же берёт и спамит просмотр своей очереди сообщений – не пришло ли что-нибудь. Да, это poling, и вы можете его вызвать асинхронно и/или с таймаутом. Затем, когда серверный сокет успешно принимает запрос, он отправляет ответ о успехе - канал связи создан. Канал – это труба м-ду сокетами. Как идёт отправка сообщений? Сперва (в нашем случае) клиент открывает свой сокет, как бы "открывая кран" и "выпуская воду" – сообщение. Затем, после его выпуска, закрывается. "Вода" идёт по каналу вплоть до серверного сокета, и сервер должен знать о приёме сообщения, поэтому серверный сокет параллельно открывается. Затем, если не будет помех, сообщение дойдёт до сервера. После его принятия, сервер прекращает ожидание и закрывает сокет. Наш же клиент отправляет запрос о дисконекте, т. е. об уничтожении канала. Сервер принимает и его, и канал уничножается. Конец!
Вот исполнение этой схемы в вашем коде:
// определяем id нашего сокета
var localEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
// создаём сокет, который работает через интернет и является потоковым
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// устанавливаем id нашего сокета
socket.Bind(localEndPoint);
// тот самый "спам" (называется прослушиванием) в нашу очередь сообщений
socket.Listen(5);
while (true)
{
// принимаем клиентский сокет (просто сокет, но с нашим/клиентским конфигом, однако с remoteEndPoint вместо localEndPoint)
var clientSocket = socket.Accept();
Span<byte> buffer = stackalloc byte[1024];
var receivedByteCount = 0;
var dataBuilder = new StringBuilder();
// мы не знаем, сколько точно байт пришло, значит нужно извлекать буферами фиксированного размера
do
{
receivedByteCount = clientSocket.Receive(buffer);
dataBuilder.Append(Encoding.UTF8.GetString(buffer.Slice(0, receivedByteCount)));
} while (clientSocket.Available > 0);
var data = dataBuilder.ToString();
Console.WriteLine(data);
socket.Send(Encoding.UTF8.GetBytes("Success!"));
// disposing убран, т. к. это задача клиента
// мы не знаем, а вдруг он ещё захочет послать сообщения?
}