Краткий теоретический материал — КиберПедия 

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Наброски и зарисовки растений, плодов, цветов: Освоить конструктивное построение структуры дерева через зарисовки отдельных деревьев, группы деревьев...

Краткий теоретический материал

2019-06-06 138
Краткий теоретический материал 0.00 из 5.00 0 оценок
Заказать работу

В качестве примера рассмотрим разработку сетевого приложения, основная концепция которого представлена на схемах.

Особенностью данного клиент-серверного приложения, безусловно, является использование классов CSocket и SSocket, которые были написаны для упрощения дальнейшего использования приложения: на базе этих классов можно реализовать практически любое несложное сетевое приложение. Основная схема работы этого программного комплекса такова:

· Запуск сервера

· Установка сервера в качестве постоянного процесса MS Windows

· Запуск клиента

· Считывание из текстового файла IP-адреса очередного сервера

· Отправление специальной команды серверу на выполнение действия screenshot made

· Выполнение на сервере данной команды

· Отправка клиентом запроса на передачу файла

· Отправка сервером файла и получение его клиентом


Класс Socket обеспечивает широкий набор методов и свойств для сетевых взаимодействий. Класс Socket позволяет выполнять как синхронную, так и асинхронную передачу данных с использованием любого из коммуникационных протоколов, имеющихся в перечислении ProtocolType.

Класс Socket придерживается шаблона имен платформы.NET Framework для асинхронных методов. Например, синхронный метод Receive соответствует асинхронным методам BeginReceive и EndReceive.

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

Если используется протокол, ориентированный на установление соединения, такой как протокол TCP, сервер должен выполнять прослушивание подключений, используя метод Listen. Метод Accept обрабатывает любые входящие запросы на подключение и возвращает объект Socket, который может использоваться для передачи данных с удаленного узла. Используйте этот возвращенный объект Socket для вызова метода Send или Receive. Вызовите метод Bind, прежде чем производить обращение к методу Listen, если необходимо указать локальный IP-адрес или номер порта. Используйте нулевое значение для номера порта, если требуется, чтобы свободный порт был назначен основным поставщиком услуг. Если требуется произвести подключение к прослушивающему узлу, вызовите метод Connect. Для обмена данными вызовите метод Send или Receive.

Если используется протокол, не ориентированный на установление соединения, такой как протокол UDP, нет необходимости в отслеживании подключений. Для приема всех поступающих датаграмм вызовите метод ReceiveFrom. Для посылки датаграмм на удаленный узел воспользуйтесь методом SendTo.

Чтобы выполнить передачи с использованием отдельных потоков во время исполнения, воспользуйтесь следующими методами, предложенными для работы в асинхронном режиме.

Если применяется протокол, ориентированный на установление соединения, такой как протокол TCP, используйте методы Socket, BeginConnect и EndConnect для подключения к прослушивающему узлу. Для асинхронного обмена данными воспользуйтесь методами BeginSend и EndSend или методами BeginReceive и EndReceive. Входящие запросы на подключение могут быть обработаны с помощью методов BeginAccept и EndAccept.

Если используется протокол без установления соединения, такой как протокол UDP, можно воспользоваться для посылки датаграмм методами BeginSendTo и EndSendTo, а для получения датаграмм можно применить методы BeginReceiveFrom и EndReceiveFrom.

 

Если на сокете выполняется несколько асинхронных операций, они не обязательно должны завершаться в том же порядке, в котором эти операции запускаются.

Когда прием и отправка данных завершены, используйте метод Shutdown для того, чтобы отключить объект Socket. После вызова метода Shutdown обратитесь к методу Close, чтобы освободить все связанные с объектом Socket ресурсы.

Класс Socket позволяет выполнить настройку объекта Socket с использованием метода SetSocketOption. Извлеките эти параметры, используя метод GetSocketOption.

Можно написать простой многопоточный HTTP-сервер для отдачи статического содержимого. Можно было бы использовать уже готовый класс HttpListener, но цель— показать, как можно сделать нечто подобное в C#.
Для начала создадим новый консольный проект:

using System;

using System.Collections.Generic;

using System.Text;

 

namespace HTTPServer

{

class Server

{

static void Main(string[] args)

{

 

}

}

}

 


В.NET можно очень легко создать TCP-сервер при помощи класса TcpListener, чем мы и воспользуемся:

class Server

{

TcpListener Listener; // Объект, принимающий TCP-клиентов

 

// Запуск сервера

public Server(int Port)

{

// Создаем "слушателя" для указанного порта

Listener = new TcpListener(IPAddress.Any, Port);

Listener.Start(); // Запускаем его

 

// В бесконечном цикле

while (true)

{

// Принимаем новых клиентов

Listener.AcceptTcpClient();

}

}

 

// Остановка сервера

~Server()

{

// Если "слушатель" был создан

if (Listener!= null)

{

// Остановим его

Listener.Stop();

}

}

 

static void Main(string[] args)

{

// Создадим новый сервер на порту 80

new Server(80);

}

}


Если сейчас запустить приложение, то уже можно будет подключиться к порту 80 и… все. Соединение будет лишь простаивать впустую, так как отсутствует его обработчик и оно не закрывается со стороны сервера.
Напишем самый простой обработчик:

// Класс-обработчик клиента

class Client

{

// Конструктор класса. Ему нужно передавать принятого клиента от TcpListener

public Client(TcpClient Client)

{

// Код простой HTML-странички

string Html = "<html><body><h1>It works!</h1></body></html>";

// Необходимые заголовки: ответ сервера, тип и длина содержимого. После двух пустых строк - само содержимое

string Str = "HTTP/1.1 200 OK\nContent-type: text/html\nContent-Length:" + Html.Length.ToString() + "\n\n" + Html;

// Приведем строку к виду массива байт

byte[] Buffer = Encoding.ASCII.GetBytes(Str);

// Отправим его клиенту

Client.GetStream().Write(Buffer, 0, Buffer.Length);

// Закроем соединение

Client.Close();

}

}


Чтобы передать ему клиента, нужно изменить одну строчку в классе Server:

// Принимаем новых клиентов и передаем их на обработку новому экземпляру класса Client

new Client(Listener.AcceptTcpClient());


Теперь можно запустить программу, открыть в браузере адрес 127.0.0.1 и увидеть большими буквами «It works!». Перед тем, как приступить к написанию парсера HTTP-запроса, сделаем наш сервер многопоточным. Для этого есть два способа: создавать вручную новый поток для каждого клиента или воспользоваться пулом потоков. У обоих способов есть свои преимущества и недостатки. Если создавать по потоку на каждого клиента, то сервер может не выдержать высокой нагрузки, но можно работать с практически неограниченным количеством клиентов одновременно. Если использовать пул потоков, то количество одновременно работающих потоков будет ограничено, но нельзя будет создать новый поток, пока не завершатся старые. Какой из способов вам больше подойдет, я не знаю, поэтому приведу пример обоих. Напишем простую процедуру потока, которая будет лишь создавать новый экземпляр класса Client:

static void ClientThread(Object StateInfo)

{

new Client((TcpClient)StateInfo);

}


Для использования первого способа нужно заменить только содержимое нашего бесконечного цикла приема клиентов:

// Принимаем нового клиента

TcpClient Client = Listener.AcceptTcpClient();

// Создаем поток

Thread Thread = new Thread(new ParameterizedThreadStart(ClientThread));

// И запускаем этот поток, передавая ему принятого клиента

Thread.Start(Client);


Для второго способа нужно проделать то же самое:

1. // Принимаем новых клиентов. После того, как клиент был принят, он передается в новый поток (ClientThread)

2. // с использованием пула потоков.

3. ThreadPool.QueueUserWorkItem(new WaitCallback(ClientThread), Listener.AcceptTcpClient());


Плюс надо установить максимальное и минимальное количество одновременно работающих потоков. Сделаем это в процедуре Main:

// Определим нужное максимальное количество потоков

// Пусть будет по 4 на каждый процессор

int MaxThreadsCount = Environment.ProcessorCount * 4;

// Установим максимальное количество рабочих потоков

ThreadPool.SetMaxThreads(MaxThreadsCount, MaxThreadsCount);

// Установим минимальное количество рабочих потоков

ThreadPool.SetMinThreads(2, 2);


Максимальное количество потоков должно быть не меньше двух, так как в это число входит основной поток. Если установить единицу, то обработка клиента будет возможна лишь тогда, когда основной поток приостановил работу (например, ожидает нового клиента или была вызвана процедура Sleep).
Итак, теперь переключимся целиком на класс Client начнем обрабатывать HTTP-запрос. Получим текст запроса от клиента:

// Объявим строку, в которой будет хранится запрос клиента

string Request = "";

// Буфер для хранения принятых от клиента данных

byte[] Buffer = new byte[1024];

// Переменная для хранения количества байт, принятых от клиента

int Count;

// Читаем из потока клиента до тех пор, пока от него поступают данные

while ((Count = Client.GetStream().Read(Buffer, 0, Buffer.Length)) > 0)

{

// Преобразуем эти данные в строку и добавим ее к переменной Request

Request += Encoding.ASCII.GetString(Buffer, 0, Count);

// Запрос должен обрываться последовательностью \r\n\r\n

// Либо обрываем прием данных сами, если длина строки Request превышает 4 килобайта

// Нам не нужно получать данные из POST-запроса (и т. п.), а обычный запрос

// по идее не должен быть больше 4 килобайт

if (Request.IndexOf("\r\n\r\n") >= 0 || Request.Length > 4096)

{

break;

}

}


Далее осуществляем парсинг полученных данных:

// Парсим строку запроса с использованием регулярных выражений

// При этом отсекаем все переменные GET-запроса

Match ReqMatch = Regex.Match(Request, @"^\w+\s+([^\s\?]+)[^\s]*\s+HTTP/.*|");

 

// Если запрос не удался

if (ReqMatch == Match.Empty)

{

// Передаем клиенту ошибку 400 - неверный запрос

SendError(Client, 400);

return;

}

 

// Получаем строку запроса

string RequestUri = ReqMatch.Groups[1].Value;

 

// Приводим ее к изначальному виду, преобразуя экранированные символы

// Например, "%20" -> " "

RequestUri = Uri.UnescapeDataString(RequestUri);

 

// Если в строке содержится двоеточие, передадим ошибку 400

// Это нужно для защиты от URL типа http://example.com/../../file.txt

if (RequestUri.IndexOf("..") >= 0)

{

SendError(Client, 400);

return;

}

 

// Если строка запроса оканчивается на "/", то добавим к ней index.html

if (RequestUri.EndsWith("/"))

{

RequestUri += "index.html";

}


Ну и наконец осуществим работу с файлами: проверим, есть ли нужный файл, определим его тип содержимого и передадим его клиенту.

string FilePath = "www/" + RequestUri;

 

// Если в папке www не существует данного файла, посылаем ошибку 404

if (!File.Exists(FilePath))

{

SendError(Client, 404);

return;

}

 

// Получаем расширение файла из строки запроса

string Extension = RequestUri.Substring(RequestUri.LastIndexOf('.'));

 

// Тип содержимого

string ContentType = "";

 

// Пытаемся определить тип содержимого по расширению файла

switch (Extension)

{

case ".htm":

case ".html":

ContentType = "text/html";

break;

case ".css":

ContentType = "text/stylesheet";

break;

case ".js":

ContentType = "text/javascript";

break;

case ".jpg":

ContentType = "image/jpeg";

break;

case ".jpeg":

case ".png":

case ".gif":

ContentType = "image/" + Extension.Substring(1);

break;

default:

if (Extension.Length > 1)

{

ContentType = "application/" + Extension.Substring(1);

}

else

{

ContentType = "application/unknown";

}

break;

}

 

// Открываем файл, страхуясь на случай ошибки

FileStream FS;

try

{

FS = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);

}

catch (Exception)

{

// Если случилась ошибка, посылаем клиенту ошибку 500

SendError(Client, 500);

return;

}

 

// Посылаем заголовки

string Headers = "HTTP/1.1 200 OK\nContent-Type: " + ContentType + "\nContent-Length: " + FS.Length + "\n\n";

byte[] HeadersBuffer = Encoding.ASCII.GetBytes(Headers);

Client.GetStream().Write(HeadersBuffer, 0, HeadersBuffer.Length);

 

// Пока не достигнут конец файла

while (FS.Position < FS.Length)

{

// Читаем данные из файла

Count = FS.Read(Buffer, 0, Buffer.Length);

// И передаем их клиенту

Client.GetStream().Write(Buffer, 0, Count);

}

// Закроем файл и соединение

FS.Close();

Client.Close();


Также в коде упоминалась пока не описанная процедура SendError. Напишем и ее:

// Отправка страницы с ошибкой

private void SendError(TcpClient Client, int Code)

{

// Получаем строку вида "200 OK"

// HttpStatusCode хранит в себе все статус-коды HTTP/1.1

string CodeStr = Code.ToString() + " " + ((HttpStatusCode)Code).ToString();

// Код простой HTML-странички

string Html = "<html><body><h1>" + CodeStr + "</h1></body></html>";

// Необходимые заголовки: ответ сервера, тип и длина содержимого. После двух пустых строк - само содержимое

string Str = "HTTP/1.1 " + CodeStr + "\nContent-type: text/html\nContent-Length:" + Html.Length.ToString() + "\n\n" + Html;

// Приведем строку к виду массива байт

byte[] Buffer = Encoding.ASCII.GetBytes(Str);

// Отправим его клиенту

Client.GetStream().Write(Buffer, 0, Buffer.Length);

// Закроем соединение

Client.Close();

}

 

Реализуем клиент-серверное приложение:

Сервер

 

using System;

using System.Net.Sockets;

using System.Net;

using System.Collections.Generic;

using System.Text;

 

namespace SServer

{

public class SServer

{

   public static void Main(string[] args)

   {

       byte[] bytes = new byte[1024];

       //Устанавливаем для сокета локальную конечную точку

       IPHostEntry ipHost = Dns.Resolve("localhost");

       IPAddress ipAddr = ipHost.AddressList[0];

       IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

       //Создаем сокет TCP\IP

       Socket sListener = new Socket(AddressFamily.InterNetwork,

            SocketType.Stream, ProtocolType.Tcp);

       //Назначаем сокет локальной конечной точку

       // и слушаем входящие сокеты

       try

       {

           sListener.Bind(ipEndPoint);

           sListener.Listen(10);

           //Начинаем слущать соединения

 

           while (true)

           {

               Console.WriteLine("Waiting for connections... ", ipEndPoint);

               //программа приостанавливается,ожидая входящее соединение

               Socket handler = sListener.Accept();

               string data = null;

               //дождались клиента,пытающегося с нами соединиться 

               int bytesRec = handler.Receive(bytes);

               data += Encoding.ASCII.GetString(bytes, 0, bytesRec);

               //выводим данные на консоль

               Console.WriteLine("Сlient Message: {0}", data);

               string theReply = "Thank You for your message " + data.Length.ToString() + " characters...I'm the Server!!!";

               byte[] msg = Encoding.ASCII.GetBytes(theReply);

               handler.Send(msg);

               handler.Shutdown(SocketShutdown.Both);

               handler.Close();

           }

 

       }

       catch (Exception e)

       {

           Console.WriteLine(e.ToString());

       }

   }

}

}

Клиент

 

using System;

using System.Net.Sockets;

using System.Net;

using System.Collections.Generic;

using System.Text;

 

namespace SClient

{

public class SClient

{

   public static void Main(string[] args)

   {

       byte[] bytes = new byte[1024];

       //Соединяемся с удаленным устройством

       try

       {

           //Устанавливаем удаленную конечную точку для сокета

           IPHostEntry ipHost = Dns.Resolve("127.0.0.1");

           IPAddress ipAddr = ipHost.AddressList[0];

           IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

           Socket sender = new Socket(AddressFamily.InterNetwork,

               SocketType.Stream, ProtocolType.Tcp);

           //Соединяем сокет с удаленной конечной точкой

           sender.Connect(ipEndPoint);

           Console.WriteLine("Connection... {0}",

               sender.RemoteEndPoint.ToString());

           string theMessage = "Knock... Knock... Follow the white rabbit:)))";

           byte[] msg = Encoding.ASCII.GetBytes(theMessage + " < The End > ");

           //отправляем данные через сокет

           int bytesSent = sender.Send(msg);

           //Получаем ответ от удаленного устройства

           int bytesRec = sender.Receive(bytes);

           Console.WriteLine("Server says: {0}",

               Encoding.ASCII.GetString(bytes, 0, bytesRec));

           //Освобождаем сокет

           sender.Shutdown(SocketShutdown.Both);

           sender.Close();

           Console.Read();

       }

       catch (Exception e)

       {

           Console.WriteLine(e.ToString());

       }

   }

}

}

 

Варианты заданий.

1. Разработать приложение, обеспечивающее мониторинг работы сервера. Серверное приложение принимает входящие сокеты. Клиентское приложение с периодичностью в 1 секунду посылает запросы (ping server). Если сервер недоступен, клиентская программа выводит соответствующее сообщение с подсчетом времени простоя в секундах.

2. Разработать приложение, обеспечивающее получение сообщений от нескольких клиентов. Каждый клиент обладает уникальным идентификатором. Для получения сообщения от клиента на сервере требуется подтверждение. В случае отказа клиент должен быть уведомлен и может послать сообщение повторно. На клиенте можно установить «режим сертифицированной передачи данных» с помощью передачи серверу некоего служебного сообщения, которое сервер принимает в любом случае без подтверждения.

3. Разработать приложение, обеспечивающее подключение клиента. Для установления связи с сервером требуется передача некоего служебного сообщения. После установления связи на сервере и у клиента выводится информация, что подключение установлено. В это время через форму клиентского приложения можно отправлять сообщения на сервер. Завершение связи (разрыв соединения) может быть осуществлено как клиентом, так и сервером (аналогично подключению) с соответствующими уведомлениями.

4. Разработать приложение, обеспечивающее рассылку сообщений. К серверу подключаются несколько клиентов. Сервер с периодичностью в 5 секунд рассылает сообщения всем клиентам с указанием текущего (серверного) времени.

 

Методика выполнения работы

· Ознакомиться с кратким теоретическим материалом;

· определить номер выполняемого варианта;

· для заданного варианта разработать консольное приложение:

· для заданного варианта выполнить программу. Результат выполнения программы необходимо отразить в протоколе выполнения программы

Порядок выполнения работы

· Изучить панели инструментов и принципы размещения и группировки окон среды. Изучить свойства проекта и решения.

· Создать проект и основной программный файл.

· Скомпилировать и запустить проект.

· Составить отчет.

 

Содержание отчета

Отчет должен содержать следующее:

· титульный лист с указанием наименования и цели занятия; фамилии, имени, отчества и номера группы студента; а также номер выполняемого варианта;

· дату выполнения работы;

· цель занятия;

· условие предложенной задачи;

· реализацию соответствующего алгоритма;

· листинг программы;

· выводы о проделанной работе.

 


Поделиться с друзьями:

Своеобразие русской архитектуры: Основной материал – дерево – быстрота постройки, но недолговечность и необходимость деления...

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

Историки об Елизавете Петровне: Елизавета попала между двумя встречными культурными течениями, воспитывалась среди новых европейских веяний и преданий...

Наброски и зарисовки растений, плодов, цветов: Освоить конструктивное построение структуры дерева через зарисовки отдельных деревьев, группы деревьев...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.207 с.