Книга: C# для профессионалов. Том II
Библиотека классов, использующая сокеты
Библиотека классов, использующая сокеты
В системе Windows 2000 Server можно установить службы простого TCP/IP как компоненты Windows. Частью служб простого TCP/IP является сервер TCP/IP "цитата дня" ("quote of the day", кратко называемая "qotd"). Эта простая служба слушает порт 17 и отвечает на каждый запрос случайным сообщением из файла c:winntsystem32driversetcquotes
. Здесь будет создан аналогичный сервер. Только этот сервер возвращает строку Unicode, в отличие от старого qotd, который возвращает код ASCII.
Постепенно начнем создавать исходный код класса QuoteServer
в файле QuoteServer.cs
:
using System;
using System.IO;
using System.Threading;
using System.Net.Sockets;
using System.Text;
using System.Collections.Specialized;
namespace Wrox.ProfessionalCSharp {
/// <summary>
/// Пример сервера сокета.
/// </summary>
public class QuoteServer {
private TcpListener listener;
private int port;
private string filename;
private StringCollection quotes;
private Random random;
private Thread listenerThread;
Конструктор QuoteServer()
является перезагруженным, чтобы имя файла и порт можно было передать в вызове. Конструктор, где передается только имя файла, использует для сервера по умолчанию порт 7890. Конструктор по умолчанию определяет имя файла по умолчанию для цитат как quotes.txt
:
public QuoteServer() : this("quotes.txt") {}
является вспомогательным методом, который читает все цитаты из файла, который был определен в конструкторе. Все цитаты добавляются в
public QuoteServer(string filename) : this(filename, 7890) {}
public QuoteServer(string filename, int port) {
this.filename = filename;
this.Port = port;
}
ReadQuotes()StringCollection quotes
. Кроме того создается экземпляр класса Random
, который будет использоваться для возврата случайных цитат:
protected void ReadQuotes() {
quotes = new StringCollection();
Stream stream = File.OpenRead(filename);
StreamReader StreamReader = new StreamReader(stream);
string quote;
while ((quote = streamReader.ReadLine()) != null) {
quotes.Add(quote);
}
streamReader.Close(); stream.Close();
random = new Random();
}
Другим вспомогательным методом является GetRandomQuoteOfTheDay()
. Этот метод возвращает случайную цитату из цитат StringCollection quotes
:
protected string GetRandomQuoteOfTheDay() {
int index = random.Next(0, quotes.Count); return quotes[index];
}
В методе Start()
весь файл, содержащий цитаты, в StringCollection quotes
считывается с помощью вспомогательной функции ReadQuotes()
. После этого запускается новый поток выполнения, который незамедлительно вызывает метод Listener()
. Мы используем поток выполнения, так как метод Start()
не может блокироваться и ждать клиента, он должен сразу же вернуть управление вызывающей стороне (SCM). SCM предположит, что запуск отказал, если метод не вернет своевременно управление вызывающей стороне.
public void Start() {
ReadQuotes();
listenerThread = new Thread(new ThreadStart(this.Listener));
listenerThread.Start();
}
Функция потока выполнения Listener()
создает экземпляр TCPListener
. В методе AcceptSocket()
ожидается соединение клиента. Как только клиент соединяется, AcceptSocket()
возвращает управление с сокетом, связанным с клиентом. Метод GetRandomQuoteOfTheDay()
вызывается для отправки возвращаемой случайной цитаты клиенту с помощью socket.Send()
:
protected void Listener() {
listener = new TcpListener(port);
listener.Start();
while (true); {
Socket socket = listener.AcceptSocket();
if (socket == null) {
return;
}
string message = GetRandomQuoteOfTheDay();
UnicodeEncoding encoder = new UnicodeEncoding();
byte [] buffer = encoder.GetBytes(message);
socket.Send(buffer, buffer.Length, 0);
socket.Close();
}
}
Помимо Start()
существуют другие методы для управления службой: Stop()
, Suspend()
и Resume()
:
public void Stop() {
listener.Stop();
}
public void Suspend() {
listenerThread.Suspend();
}
public void Resume() {
listenerThread.Resume();
}
Методом, который будет открыто доступным, является RefreshQuotes()
. Если содержащий цитаты файл изменяется, то запускается повторное чтение данного файла с помощью этого метода:
public void RefreshQuotes() {
ReadQuotes();
}
}
}
Прежде чем разрабатывать службу для нашего сервера, полезно будет создать тестовую программу, которая имеет экземпляр QuoteServer
и вызывает Start()
. Таким образом можно протестировать функциональность без необходимости обрабатывать специфические для службы вопросы. Можно сконцентрироваться на создании требуемой функциональности. Этот тестовый сервер должен запускаться вручную и код легко просматривается с помощью отладчика.
Тестовая программа является консольным приложением C#. Мы ссылаемся на сборку класса QuoteServer
. Содержащий цитаты класс копируется в каталог с:wrox
(или нужно изменить аргумент конструктора для определения, куда копируется файл). После вызова конструктора мы обращаемся к методу Start()
экземпляра QuoteServer.Start()
возвращает управление сразу после создания потока выполнения, поэтому консольное приложение продолжает выполняться до тех пор, пока не будет нажата клавиша Return
:
static void Main(string[] args) {
QuoteServer qs = new QuoteServer(@"c:wroxquotes.txt", 4567);
qs.Start();
Console.WriteLine("Hit return to exit");
Console.ReadLine();
qs.Stop();
}
Отметим, что QuoteServer
с помощью этой программы будет выполняться на порте 4567 на localhost
и необходимо использовать эти настройки позже на клиенте.
- 9.7.4. Иерархии классов и абстрактные классы
- 17.2.1. Библиотека setup.rb
- 15.2.1. Стандартная библиотека rss
- Библиотека Ext Core
- А.2.4. Библиотека ccmalloc
- 8.8.5. Шаг 4. Задание интерфейсов классов
- У14.4 Наследование без классов
- Пример: функция readline, использующая собственные данные потока
- Глава 15 Сокеты
- Глава 4 Элементарные сокеты TCP
- 13.2. Локали и библиотека С
- 15.5.1. Библиотека dbug — усовершенствованный printf()