Сетевое программирование в .NET, страница 25

¨  Слушатель использует специальный IP-адрес—элемент перечисления: IPAddress.Any (мы могли бы задать localhost). Это означает, что при определении адреса своего конца соединения (local endpoint) он полагается на скрытый внутри него service provider.

¨  Метод Start запускает цикл прослушивания сети. Теперь каждый пришедший в порт запрос на соединение слушатель помещает в специальную очередь до тех пор, пока не будет вызван метод Stop (или число запросов в очереди не превысит MaxConnections). Заметьте, что запросы могут сыпаться градом и сервер может не успевать обрабатывать их в режиме реального времени, поэтому он использует очередь. Аналогом методу Start в технологии Winsock являются две API-функции: bind и listen. Мы их использовали ранее.

¨  Метод AcceptSocket (или AcceptTcpClient, так как на сервере вместо Socket'а тоже можно использовать другой класс—TcpClient) выбирает соединение из очереди запросов и обрабатывают его. Если очередь запросов пуста, то AcceptSocket замораживает приложение.

Режим замораживания (ожидания) обозначают термином blocking mode (режим блокировки). Сервер ничего не может делать, пока не придет запрос на обслуживание от какого-либо клиента. Заметьте, что метод recv в приложении Winsock делал то же самое. Во избежание блокировки MSDN предлагает использовать такой алгоритм.

listener.Start();

if (listener.Pending()) // Если очередь не пуста, обслуживаем клиентов

{

  socket = listener.AcceptSocket();

  . . .// и  т. д.

}

else

{

        // Делаем что-то другое

}

Наш сервер не собирается делать ничего другого, он полностью зациклен в выполнении метода AcceptSocket и ждет поступления запросов от клиентов по порту 27015 (вспомните, что говорилось ранее по поводу выбора номера порта). Как только придет запрос от клиента, выполнение серверного приложения возобновляется и оно:

¨  Выводит в свое консольное окно информацию об обслуживаемом клиенте,

¨  Подготавливает очередной совет дня (выбирает его из массива с помощью генератора случайных чисел),

¨  Посылает его текущему клиенту (см. код: socket.Send),

¨  Бесконечный цикл работы сервера продолжается (все серверы работают долго, пока их не выключат).

Вы можете добавить некоторые (показанные ниже) настройки сетевого разъема. Их смысл смотрите в справке.

socket.LingerState = new LingerOption (true, 10);

socket.NoDelay = true;

socket.SendTimeout = 1000;

socket.Ttl = 42;

Разработка клиентского приложения

В разрабатываемом нами сценарии роль клиента проще, чем в приложении Winsock. Клиент получает услугу (совет дня) и выходит из игры. Для получения следующего совета ему надо вновь создать соединение с сервером. Такой поворот событий вы реализуете самостоятельно, а в том подходе, что реализован ниже, клиент ждет ввода произвольной строки (чтобы задержать приложение и вы успели увидеть результат общения с сервером) и выключается. Наша цель, повторяю, не в том, чтобы организовать chat (обмен сообщениями между клиентом и сервером), а в том, чтобы показать, как один сервер обслуживает произвольное количество клиентов.

class SocketClient

{

public static void Main()

{

  int port = 27015;

  string

    host = "localhost",    // Имя сервера (Вы можете использовать другое, реальное имя)

    header = "Trying to connect to: " + host + " on port: " + port.ToString(),

    line = "\n" + new string('\u2500', header.Length) + "\n";

  Console.WriteLine (header + line);

  TcpClient client = null;

  try

  {

    client = new TcpClient();

    client.Connect (host, port);

    byte[] buffer = new byte[512];

    int bytes = client.GetStream().Read(buffer, 0, buffer.Length);

    string tip = Encoding.ASCII.GetString(buffer).Substring(0, bytes);

    Console.WriteLine("Received {0} bytes:\n-->{1}", bytes, tip);

  }

  catch (Exception ex)

  {

    Console.WriteLine(ex.Message);

    return;

  }

  finally

  {

    if (client != null)

      client.Close();

  }

  Console.ReadLine();