Книга: 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 и необходимо использовать эти настройки позже на клиенте.

Оглавление книги


Генерация: 1.353. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз