Свой сервер и клиент к нему при помощи TcpListener и TcpClient

Не так давно, наткнулся на одну статейку на Хабре, по поводу TcpListener — там с его помощью делался Web-сервер. Поскольку есть некоторые задачи требующие создания подобного приложения, было решено изучить вопрос и попробовать воссоздать увиденное. Переписал что было в публикации в приложение (всегда так делаю прежде чем скачать исходник — лучше для понимания) запустил, не работает. Точнее работает но через раз — как то кривенько и для реально рабочего приложения не годится. Скачал исходники — тоже самое, работает но через раз. Здесь работает через раз — означает что клиент то цеплялся то не цеплялся к серверу. Решил изучить вопрос с ноля. Накопал некоторый материал в интернете, всё это переработал — получилось тестовое рабочее приложение.

Начну с того, что я хотел сделать: пример состоит из двух приложений — сервера и клиента. (Мне Web-сервер не особо интересен, хотя пример можно достаточно просто переделать в него.)

Сервер — принимает данные от клиента (текст), преобразует строку полученную от клиента к заглавным буквам и отправляет обратно клиенту.

Клиент — отправляет n-ное количество раз заданную строку клиенту (сделано чтобы поназапускав несколько клиентов, в том числе с разных машин, посмотреть как будет себя вести сервер) прибавляя порядковый номерок к посланному тексту и получает такое же количество раз ответ от сервера с текстом заглавными буквами.

Дополнительно — вначале пример был без использования ThreadPool, но потом решил добавить, так сервер должен работать стабильнее (по моему мнению) и обрабатывать большее количество одновременных запросов.

Поскольку код я прокомментировал довольно хорошо, то привожу как есть, без дополнительного описания.

Сервер:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace ExampleTcpListener_Console
{
    class ExampleTcpListener
    {
        static void Main(string[] args)
        {
            TcpListener server = null;
            try
            {
                // Определим нужное максимальное количество потоков
                // Пусть будет по 4 на каждый процессор
                int MaxThreadsCount = Environment.ProcessorCount * 4;
                Console.WriteLine(MaxThreadsCount.ToString());
                // Установим максимальное количество рабочих потоков
                ThreadPool.SetMaxThreads(MaxThreadsCount, MaxThreadsCount);
                // Установим минимальное количество рабочих потоков
                ThreadPool.SetMinThreads(2, 2);


                // Устанавливаем порт для TcpListener = 9595.
                Int32 port = 9595;
                IPAddress localAddr = IPAddress.Parse("127.0.0.1");
                int counter = 0;
                server = new TcpListener(localAddr, port);

                // Запускаем TcpListener и начинаем слушать клиентов.
                server.Start();

                // Принимаем клиентов в бесконечном цикле.
                while (true)
                {
                    
                    Console.Write("\nWaiting for a connection... ");

                    // При появлении клиента добавляем в очередь потоков его обработку.
                    ThreadPool.QueueUserWorkItem(ObrabotkaZaprosa,server.AcceptTcpClient());
                    // Выводим информацию о подключении.
                    counter++;
                    Console.Write("\nConnection №" + counter.ToString() + "!");
                    
                }
            }
            catch (SocketException e)
            {
                //В случае ошибки, выводим что это за ошибка.
                Console.WriteLine("SocketException: {0}", e);
            }
            finally
            {
                // Останавливаем TcpListener.
                server.Stop();
            }


            Console.WriteLine("\nHit enter to continue...");
            Console.Read();
        }

        static void ObrabotkaZaprosa(object client_obj)
        {
            // Буфер для принимаемых данных.
            Byte[] bytes = new Byte[256];
            String data = null;
            
            //Можно раскомментировать Thread.Sleep(1000); 
            //Запустить несколько клиентов
            //и наглядно увидеть как они обрабатываются в очереди. 
            //Thread.Sleep(1000);

            TcpClient client = client_obj as TcpClient;
            
            data = null;
            
            // Получаем информацию от клиента
            NetworkStream stream = client.GetStream();

            int i;

            // Принимаем данные от клиента в цикле пока не дойдём до конца.
            while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
            {
                // Преобразуем данные в ASCII string.
                data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                
                // Преобразуем строку к верхнему регистру.
                data = data.ToUpper();
                
                // Преобразуем полученную строку в массив Байт.
                byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                // Отправляем данные обратно клиенту (ответ).
                stream.Write(msg, 0, msg.Length);
                
            }

            // Закрываем соединение.
            client.Close();


        }
    }
}


Клиент:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace ExampleTcpClient
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("\n Позиция № "+i.ToString()+"\n");
                Connect("127.0.0.1", "Hello World! №"+i.ToString());
            }
            Console.WriteLine("\n Press Enter to continue...");
            Console.Read();
        }

        static void Connect(String server, String message)
        {
            try
            {
                // Создаём TcpClient.
                // Для созданного в предыдущем проекте TcpListener 
                // Настраиваем его на IP нашего сервера и тот же порт.
                
                Int32 port = 9595;
                TcpClient client = new TcpClient(server, port);

                // Переводим наше сообщение в ASCII, а затем в массив Byte.
                Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

                // Получаем поток для чтения и записи данных.
                NetworkStream stream = client.GetStream();

                // Отправляем сообщение нашему серверу. 
                stream.Write(data, 0, data.Length);
                Console.WriteLine("Sent: {0}", message);

                // Получаем ответ от сервера.

                // Буфер для хранения принятого массива bytes.
                data = new Byte[256];

                // Строка для хранения полученных ASCII данных.
                String responseData = String.Empty;

                // Читаем первый пакет ответа сервера. 
                // Можно читать всё сообщение.
                // Для этого надо организовать чтение в цикле как на сервере.
                Int32 bytes = stream.Read(data, 0, data.Length);
                responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
                Console.WriteLine("Received: {0}", responseData);

                // Закрываем всё.
                stream.Close();
                client.Close();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }

            
        }
    }
}


Вот что получилось:
Пример использования TcpListener

Исходники можно скачать здесь: ExampleTcpListener.rar




2 комментария

avatar
Большое спасибо за этот пост. Наконец то я нашел наглядный пример на эту тему, который одновременно и работает и хорошо комментирован. Знаю, что прошло больше трёх лет, но спасибо)
avatar
Пожалуйста. :)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.