В прошлой теме была рассмотрена однонаправленная связь между клиентом и сервером: когда либо сервер отправляет данные, а клиент получае, либо, наоборот, клиент отправляет, а сервер получает. Рассмотрим более сложный пример, когда и клиент, и сервер одновременно отправляют и получают данные.
Пусть код сервера выглядит следующим образом:
using System.Net; using System.Net.Sockets; using System.Text; var tcpListener = new TcpListener(IPAddress.Any, 8888); var words = new Dictionary<string, string>() { {"red", "красный" }, {"blue", "синий" }, {"green", "зеленый" } }; try { tcpListener.Start(); // запускаем сервер Console.WriteLine("Сервер запущен. Ожидание подключений... "); while (true) { // получаем подключение в виде TcpClient using var tcpClient = await tcpListener.AcceptTcpClientAsync(); // получаем объект NetworkStream для взаимодействия с клиентом var stream = tcpClient.GetStream(); // буфер для входящих данных var response = new List<byte> (); int bytesRead = 10; while(true) { // считываем данные до конечного символа while ((bytesRead = stream.ReadByte()) != '\n') { // добавляем в буфер response.Add((byte)bytesRead); } var word = Encoding.UTF8.GetString(response.ToArray()); // если прислан маркер окончания взаимодействия, // выходим из цикла и завершаем взаимодействие с клиентом if (word == "END") break; Console.WriteLine($"Запрошен перевод слова {word}"); // находим слово в словаре и отправляем обратно клиенту if (!words.TryGetValue(word, out var translation)) translation = "не найдено в словаре"; // добавляем символ окончания сообщения translation += '\n'; // отправляем перевод слова из словаря await stream.WriteAsync(Encoding.UTF8.GetBytes(translation)); response.Clear(); } } } finally { tcpListener.Stop(); }
В данном случае мы предполагаем, что сервер будет выполнять роль своего рода словаря - получать от клиента слово и отправлять обратно его перевод. Для хранения данных определен тестовый словарь words:
var words = new Dictionary<string, string>() { {"red", "красный" }, {"blue", "синий" }, {"green", "зеленый" } };
При получении и обработке запросов сервер применяет простой прокотол из двух правил: каждое отдельное сообщение клиенту и серверу должно оканчиваться символом \n, а маркер окончания подключения должен представлять строку "END". В соответствии с этими правилами сервер сначала считывает запрос сервера и получаем слово, для которого надо выполнить перевод:
while ((bytesRead = stream.ReadByte()) != '\n') { response.Add((byte)bytesRead); } var word = Encoding.UTF8.GetString(response.ToArray());
Если отправлена строка "END", выходим из цикла и тем самым прекращаем работу с текущим клиентом:
if (word == "END") break;
Иначе находим в словаре перевод слова (при его наличии) и отправляем клиенту:
if (!words.TryGetValue(word, out var translation)) translation = "не найдено в словаре"; translation += '\n'; await stream.WriteAsync(Encoding.UTF8.GetBytes(translation));
При этом опять же в соответствии с условным протоколом при отправке добавляем к сообщению символ \n, благодаря чему клиент будет знать, что это окончание слова.
Теперь создадим тестового клиента для выше определенного сервера:
using System.Net.Sockets; using System.Text; using TcpClient tcpClient = new TcpClient(); await tcpClient.ConnectAsync("127.0.0.1", 8888); // слова для отправки для получения перевода var words = new string[] { "red", "yellow", "blue" }; // получаем NetworkStream для взаимодействия с сервером var stream = tcpClient.GetStream(); // буфер для входящих данных var response = new List<byte>(); int bytesRead = 10; // для считывания байтов из потока foreach (var word in words) { // считыванием строку в массив байт // при отправке добавляем маркер завершения сообщения - \n byte[] data = Encoding.UTF8.GetBytes(word + '\n'); // отправляем данные await stream.WriteAsync(data); // считываем данные до конечного символа while ((bytesRead = stream.ReadByte()) != '\n') { // добавляем в буфер response.Add((byte)bytesRead); } var translation = Encoding.UTF8.GetString(response.ToArray()); Console.WriteLine($"Слово {word}: {translation}"); response.Clear(); } // отправляем маркер завершения подключения - END await stream.WriteAsync(Encoding.UTF8.GetBytes("END\n")); Console.WriteLine("Все сообщения отправлены");
Здесь для теста определяем массив из трех слов, перевод которых мы собираемся получить:
var words = new string[] { "red", "yellow", "blue" };
Пробегаемся по этому массиву и отправляем каждое слово серверу:
foreach (var word in words) { // при отправке добавляем маркер завершения сообщения - \n byte[] data = Encoding.UTF8.GetBytes(word + '\n'); await stream.WriteAsync(data);
Опять же в соответствии с принятым нами протоколом каждое слово завершается символом \n.
Затем считываем ответ сервера и получаем перевод слова:
while ((bytesRead = stream.ReadByte()) != '\n') { response.Add((byte)bytesRead); } var translation = Encoding.UTF8.GetString(response.ToArray());
Для завершения посылаем сервер маркер окончания подключения:
await stream.WriteAsync(Encoding.UTF8.GetBytes("END\n"));
Запустим сервер и клиент. При получении запросов консоль сервера отобразит запрошенные слова на перевод:
Сервер запущен. Ожидание подключений... Запрошен перевод слова red Запрошен перевод слова yellow Запрошен перевод слова blue
А консоль клиента отобразит полученный от сервера перевод запрошенных слов:
Слово red: красный Слово yellow: не найдено в словаре Слово blue: синий Все сообщения отправлены
Тот же пример только на чистых сокетах с использованием класса Socket. Код сервера:
using System.Net; using System.Net.Sockets; using System.Text; using Socket tcpListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var words = new Dictionary<string, string>() { { "red", "красный" }, { "blue", "синий" }, { "green", "зеленый" }, }; try { tcpListener.Bind(new IPEndPoint(IPAddress.Any, 8888)); tcpListener.Listen(); // запускаем сервер Console.WriteLine("Сервер запущен. Ожидание подключений... "); while (true) { // получаем подключение в виде TcpClient using var tcpClient = await tcpListener.AcceptAsync(); // буфер для накопления входящих данных var response = new List<byte>(); // буфер для считывания одного байта var bytesRead = new byte[1]; while (true) { // считываем данные до конечного символа while (true) { var count = tcpClient.Receive(bytesRead); // смотрим, если считанный байт представляет конечный символ, выходим if (count == 0 || bytesRead[0] == '\n') break; // иначе добавляем в буфер response.Add(bytesRead[0]); } var word = Encoding.UTF8.GetString(response.ToArray()); // если прислан маркер окончания взаимодействия, // выходим из цикла и завершаем взаимодействие с клиентом if (word == "END") break; Console.WriteLine($"Запрошен перевод слова {word}"); // находим слово в словаре и отправляем обратно клиенту if (!words.TryGetValue(word, out var translation)) translation = "не найдено в словаре"; // добавляем символ окончания сообщения translation += '\n'; // отправляем перевод слова из словаря await tcpClient.SendAsync(Encoding.UTF8.GetBytes(translation)); response.Clear(); } } } catch(Exception ex) { Console.WriteLine(ex.Message); }
И код клиента:
using System.Net.Sockets; using System.Text; // слова для отправки для получения перевода var words = new string[] { "red", "yellow", "blue" }; using var tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { await tcpClient.ConnectAsync("127.0.0.1", 8888); // буфер для входящих данных var response = new List<byte>(); foreach (var word in words) { // считыванием строку в массив байт // при отправке добавляем маркер завершения сообщения - \n byte[] data = Encoding.UTF8.GetBytes(word + '\n'); // отправляем данные await tcpClient.SendAsync(data); // буфер для считывания одного байта var bytesRead = new byte[1]; // считываем данные до конечного символа while (true) { var count = tcpClient.Receive(bytesRead); // смотрим, если считанный байт представляет конечный символ, выходим if (count == 0 || bytesRead[0] == '\n') break; // иначе добавляем в буфер response.Add(bytesRead[0]); } var translation = Encoding.UTF8.GetString(response.ToArray()); Console.WriteLine($"Слово {word}: {translation}"); response.Clear(); } // отправляем маркер завершения подключения - END await tcpClient.SendAsync(Encoding.UTF8.GetBytes("END\n")); Console.WriteLine("Все сообщения отправлены"); } catch (Exception ex) { Console.WriteLine(ex.Message); }