QuoteServer
содержит реальную функциональность. Мы прочитаем файл цитат в кэш памяти и будем отвечать на запросы цитат с помощью сервера сокета.
QuoteСlient
является клиентским приложением Windows Forms. Это приложение создает клиентский сокет для коммуникации с QuoteServer
. Третья сборка является реальной службой. QuoteService
запускает и останавливает QuoteServer
, служба будет управлять сервером:

Прежде чем разработать служебную часть программы, создадим простой сервер сокета в дополнительной библиотеке классов C#, которая будет использоваться из процесса службы.
Библиотека классов, использующая сокеты
В системе 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();
}