Реализация сервера

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

// Настройка серверного приложения, принимающего запрос на соединение, с отправкой

// строки клиенту и закрытие соединения

using System;

using System.Drawing;

using System.Collection;

using System.ComponentModel;

using System.Windows.Forms;

using System.Threading;

using System.Net.Sockets;

using System.IO;

// Класс сервера, ожидающего соединения с клиентами и обеспечивающий "разговор"

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

public class Server: System.Windows.Forms System.Windows.Forms.Form

{

TextBox inputTextBox, displayTextBox;

Socket connection;

Thread readThread;

Container components = null;

NetworkStream socketStream;

BinaryWriter writer;

BinaryReader reader;

public Server() // Конструктор по умолчанию

{

InitializeComponent();

// Создание нового потока от сервера

readThread = new Thread(new ThreadStart(RunServer));

readThread.Start();

}

[STAThread]

Static void Main(){ Application.Run(new Server()); }

protected void Server_Closing(object sender, CancelEventArg e)

{ Environment.exit(Environment.ExitCode); }

// Отправка клиенту текста, набранного на сервере

protected void inputTextBox_KeyDown(object sender, KeyEventArg e)

{

if(e.KeyCode == Keys.Enter && connection!= null)

{

Writer.Write("SERVER>>> " + inputTextBox.Text;

displayTextBox += "\r\nServer>>> + inputTextBox.Text;

//Если пользователь на сервер указал клиенту на окончание соединения

if(inputTextBox.Text == "TERMINATE") Connection.Close();

inputTextBox.Clear();

}

}

// Метод, позволяющий клиенту подключиться к серверу и

// отобразить отправляемый им текст

public void RunServer()

{

TcpListener listener;

int count =1;

// Ожидание клиентского соединения и отображение текста от клиента

// Шаг 1: Создание слушателя Tcp-канала

listener = new TcpListener(5000);

// Шаг 2: Listener ожидает запрос на соединение

Listener.Start();

// Шаг 3: Установка соединения по запросу клиента

while(true)

{

displayTextBox.Text = "Ожидание соединения\r\n";

// Прием запроса на соединение

Connection = Listener.AcceptSocket();

// Создание потока для сокета

socketStream = new NetworkStream(connection);

// Создание объектов для передачи данных с помощью потока

writer = new BinaryWriter(socketStream);

read = new BinaryReader(socketStream);

displayTextBox.Text += "Соединение " + counter + "установлено.\r\n";

// Информирование клиента об успешном установлении соединения

writer.Writer("SERVER>>> Соединение установлено!");

string theReplay = "";

// Шаг 4: Считывание строки данных, получаемых от клиента

do

{

// Считывание строки, отправленную для сервера

theReplay = reader.readString();

// Отображение сообщения

displayTextBox.Text +="\r\n" + theReplay;

}

while(theReplay!= "CLIENT>>> TERMINATE" &&

connection.Connected);

displayTextBox.Text += "\r\n\Пользователь разрывает соединение";

// Шаг 5: закрытие соединения

inputTextBox.ReadOnly = true;

writer.Close();

reader.Close();

socketStream.Close();

connection.Close();

++ counter;

}

}

}

В конструкторе сервера создается поток, который будет принимать от клиентов запросы на соединение. Объект потока активизируется методом RunServer (). Этот метод инициализирует сервер для получения запросов на соединение и их обработки. Далее, на шаге 1, создается класс объекта для прослушивания клиентского запроса на соединение через порт 5000. На шаге 2 вызывается метод этого объекта Start () для прослушивания запросов в канале. Затем (на шаге 3) объявляется бесконечный цикл while, определяющий поступление запросов на соединение. Прослушивание канала начинается сразу после вызова у прослушивающего объекта метода AcceptSocket, который, в случае поступления запроса, возвращает объект сокета. Дальше управлять соединением будет именно этот сокет, который передается в качестве аргумента в конструктор сетевого потока NetworkStream. Объект этого потока обеспечивает доступ к потокам сети. В данном случае он обеспечивает доступ к соединению Socket. После чего создаются объекты для бинарной записи и считывания данных. Причем, в каждый их конструктор сетевой поток передается в качестве аргумента. В текстовое поле TextBox вводится текст, указывающий на прием соединение. Для записи в поток различных данных метод Write класса BinaryWriter имеет множество его перегруженных версий.

Цикл do/while выполняется до тех пор, пока сервер не получит сообщение об окончании соединения (" CLIENT>>>TERMINATE "). Для считывания строки из потока на шаге 4 используется метод BinaryReader.ReadString (). Этот метод блокируется до тех пор, пока строка не будет считана полностью. Для предотвращения блокировки всего сервера используется отдельный поток для управления передачей информации. Цикл while повторяется до тех пор, пока имеется информация для считывания, в результате чего блокируется ввод/вывод и наблюдается частое "зависание" программы. Если же данную часть программы запустить в отдельном потоке, то пользователь может взаимодействовать с формой Windows и отправлять сообщения до тех пор, пока программа будет ожидать входящих сообщений. После окончания переписки между клиентом и сервером объекты BinaryWriter, BinaryReader, NetworkStream и Socket закрываются (шаг 5) с помощью вызова соответствующих методов Close (). После этого сервер ожидает запроса на соединение другого клиента и возвращением к циклу do/while.

Когда пользователь серверного приложения вводит строку в текстовое поле TextBox и нажимает на клавишу <Enter>, то обработчик события inputTextBox_KeyDown считывает строку и отправляет ее с помощью метода BinaryWriter.Write (). Если пользователь прекращает работу серверного приложения, тогда вызывается метод Socket.Close () для закрытия сеанса связи.

Обработчик события Server_Closing закрывает приложение и использует метод Environment.ExitCode для прерывания работы всех потоков, связанных с данным приложением.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: