Свой сервер и клиент к нему при помощи TcpListener и TcpClient C#
Не так давно, наткнулся на одну статейку на Хабре, по поводу 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);
}
}
}
}
Вот что получилось:
Исходники можно скачать здесь:
Скачать файл: exampletcplistener.rar [78.63 Kb] (cкачиваний: 11)