Главная » Просмотр файлов » А.В. Столяров - Практикум на ЭВМ - Многопользовательский игровой сервер

А.В. Столяров - Практикум на ЭВМ - Многопользовательский игровой сервер (1114951), страница 3

Файл №1114951 А.В. Столяров - Практикум на ЭВМ - Многопользовательский игровой сервер (А.В. Столяров - Практикум на ЭВМ - Многопользовательский игровой сервер) 3 страницаА.В. Столяров - Практикум на ЭВМ - Многопользовательский игровой сервер (1114951) страница 32019-05-08СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 3)

Это достигается вызовом listen():#include <sys/socket.h>int listen(int sockfd, int qlen);Параметр sockfd задает дескриптор сокета. Параметр qlen означаетмаксимальную длину очереди пришедших запросов на соединение, которые сервер еще не принял к обработке. Некоторые операционные системыне поддерживают значения qlen> 5, поэтому обычно вторым параметромвызова listen() задают просто число 5.Вызов listen() возвращает 0 в случае успеха, −1 – в случае ошибки.С учетом этого вызов может выглядеть так:12if (-1 == listen(ls, 5)) {/* Здесь следует поместить обработку ошибки */}В результате выполнения вызова listen() в системе появится слушающий сокет, который можно увидеть с помощью командыnetstat -a -n | grep LISTENКлиент, находящийся на любой машине, с которой доступна наша сеть,может установить соединение с нашим сокетом. Чтобы получить возможность обмена информацией с этим клиентом одновременно с ожиданиемзапросов на соединение от других клиентов, нам необходимо принять запрос на соединение.

Это делается с помощью вызова accept():#include <sys/types.h>#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, int *addrlen);Параметр sockfd задает дескриптор слушающего сокета, на котором следует принять соединение. Что касается параметров addr и addrlen, то онипозволяют узнать, с какого адреса исходит соединение. Информация записывается в структуру, на которую указывает указатель addr. Переменная,на которую указывает addrlen, должна перед обращением к accept() содержать длину структуры addr в байтах; после обращения в ней будетсодержаться реальная длина данных, записанных в эту структуру. Естественно, использовать следует структуру типа struct sockaddr_in (см.§2.2.2).Если информация об источнике соединения не интересна, в качествеобоих параметров addr и addrlen можно передать нулевые указатели.Если во входной очереди уже имеется запрос на соединение, вызовaccept() возвращает управление немедленно; в противном случае управление будет возвращено после того, как такой запрос будет получен.Вызов accept() возвращает файловый дескриптор, связанный с сокетом, через который будет осуществляться связь с клиентом, соединениекоторого только что было принято.Полученный файловый дескриптор должен быть неотрицательным числом.

Если вызов socket() вернул значение −1, это свидетельствует о происшедшей ошибке. Программа обязательно должна корректно обрабатывать такую ситуацию.132.3Мультиплексирование ввода-выводаПосле того, как вызов accept() успешно отработает в первый раз, в вашейпрограмме появятся два файловых дескриптора, требующих внимания.Это слушающий сокет и сокет, полученный в результате вызова accept()(сокет клиента). На слушающий сокет могут поступить новые запросы, которые необходимо принимать вызовом accept(); в то же время на сокетклиента могут поступить данные, переданные клиентом, которые необходимо прочитать с помощью вызова read().

Какое из этих событий произойдетраньше, неизвестно.Более того, в программе могут быть и другие источники событий. Так,клиентов, скорее всего, будет больше одного. Кроме того, для реализацииуправления сервером нам, возможно, потребуется организовать диалог спользователем, запустившим сервер, для чего необходима возможность обработки данных, поступающих со стандартного ввода программы-сервера.2.3.1Методы организации многопользовательского сервераСуществует несколько подходов к организации программ, работающих снесколькими источниками событий. Самый простой (и некорректный) изних состоит в организации циклического опроса всех имеющихся источников событий.

Так, все имеющиеся сокеты можно перевести в так называемый неблокирующий режим, в котором все системные вызовы, относящиесяк таким сокетам, которые не могут быть исполнены без блокирования выполнения процесса, будут возвращать управление сразу же, сигнализируяоб ошибке (в частности, вызов accept(), будучи вызванным в отсутствиенеобработанного запроса на соединение, немедленно вернет −1).Этот способ имеет фундаментальный неустранимый недостаток, заключающийся в наличии так называемого активного ожидания.

Действительно, даже если не происходит никаких событий, требующих обработки,программа-сервер будет вновь и вновь опрашивать имеющиеся дескрипторы, впустую занимая процессорное время. Естественно, пользоваться таким методом ни в коем случае не следует.Второй вариант организации многопользовательского сервера, о котором, в частности, рассказывается в основном курсе лекций “Системное программное обеспечение” в III семестре, основан на создании отдельного процесса для работы с каждым источником событий.

В этом случае послекаждого вызова accept() немедленно выполняется вызов fork(), порождающий новый процесс, и родительский процесс возвращается к обработке входящих запросов на соединение, в то время как дочерний процесс14обслуживает клиента, используя полученный от accept() дескриптор. Подробно этот механизм рассмотрен в книге [2].Такой вариант идеально подходит для случая, когда каждый клиентобслуживается отдельно от остальных и не имеет с ними никакой связи.Однако для случая многопользовательской игры, которая проходит в общем игровом пространстве, такой способ подходит заметно хуже, посколькувлечет активное использование разделяемой памяти и семафоров, что самопо себе усложняет программу3 .Третий способ называется мультиплексированием ввода-вывода иможет быть осуществлен с помощью системных вызовов select()или poll()4. В дальнейшем мы ограничимся рассмотрением функцииselect().

При желании читатель может освоить функцию poll() самостоятельно, прибегнув к литературе [3] и команде man.2.3.2Вызов select()Системный вызов select() предназначен для использования в ситуации,когда необходимо организовать работу с несколькими файловыми дескрипторами, не имея a priori информации о том, какой из дескрипторов первымпотребует внимания программы. Кроме того, возможно, требуется отслеживание некоторых событий по времени (например, тайм-аутов на сетевыхсоединениях).Прототип вызова select() выглядит следующим образом:#include <sys/time.h>#include <sys/types.h>#include <unistd.h>int select(int n, fd_setfd_setfd_setstruct*readfds,*writefds,*exceptfds,timeval *timeout);Параметры readfds, writefds и exceptfds обозначают множества файловых дескрипторов, для которых нас интересует, соответственно, возмож3Тем не менее, построить сервер таким образом вполне возможно.

Несмотря на сложности, связанные с использованием разделяемой памяти, это может быть интересно в качестве упражнения.4Вообще говоря, select() и poll() предназначены для одних и тех же действий. select() несколькопроще в работе, poll() несколько более универсален. В некоторых системах ядро реализует только одинвариант интерфейса, при этом второй эмулируется через него в виде библиотечной функции.

Так,в системе Solaris присутствует системный вызов poll(), а select() является библиотечной функцией.Кроме того, в некоторых современных системах присутствует также вызов kqueue(), реализующийальтернативный подход к выборке события.15ность немедленного чтения, возможность немедленной записи и наличиеисключительной ситуации. Параметр n указывает, какое количество элементов в этих множествах является значащим. Этот параметр необходимоустановить равным max_d+1, где max_d – максимальный номер дескриптора среди подлежищих обработке. Наконец, параметр timeout задает промежуток времени, спустя который следует вернуть управление, даже еслиникаких событий, связанных с дескрипторами, не произошло.Объект “множество дескрипторов” задается переменной типа fd_set.Внутренняя реализация переменных этого типа нас, вообще говоря, не интересует5.

Для работы с переменными этого типа система предоставляет внаше распоряжение следующие макросы:FD_ZERO(fd_set *set);/*FD_CLR(int fd, fd_set *set);/*FD_SET(int fd, fd_set *set);/*FD_ISSET(int fd, fd_set *set);/*очистить множество */убрать дескриптор из мн-ва */добавить дескриптор к мн-ву */входит ли дескр-р в мн-во? */В рассматриваемой задаче объемы передаваемых по сети данных сравнительно незначительны, что позволяет предполагать, что системные вызовы, осуществляющие запись в сокеты, никогда не будут блокироватьпрограмму-сервер. Также можно считать, что на сокетах никогда не произойдут исключительные ситуации. Таким образом, аргументы writefds иexceptfds можно не использовать (вместо них передавать вызову select()нулевые указатели).В простейшей версии программы-сервера также нет необходимости виспользовании параметра timeout6.

Поэтому можно передать нулевой указатель и в качестве пятого параметра вызова. На всякий случай отметим, чтоструктура timeval имеет два поля типа long. Поле tv_sec задает количество секунд,поле tv_usec - количество микросекунд (миллионных долей секунды).Вызов select() изменяет все переданные ему по указателюаргументы, так что перед каждым обращением к нему аргументыдолжны быть сформированы заново.Вызов select() возвращает −1 в случае возникновения ошибки. Учтите,что, если ваша программа обрабатывает те или иные сигналы, вызов select() можетвернуть −1 в случае, если его выполнение было прервано пришедшим сигналом. Приэтом значением переменной errno будет EINTR, что свидетельствует о нормальном ходе событий.

Вы можете не учитывать данный комментарий, если ваша программа не5Для различных систем она может оказаться разной.Необходимость использования этого параметра возникает при выполнении некоторых дополнительных задач.616перехватывает никаких сигналов.

Вызов возвращает значение 0 в случае, еслипричиной выхода из вызова стало наступление заданного таймаута. Есливызов возвратил положительное число, оно означает количество дескрипторов, для которых произошло какое-то событие.После возврата из вызова select() переданные ему переменные типаfd_set оказываются модифицированы. Если при входе в вызов эти множества содержали дескрипторы, относительно которых нас интересует информация о событиях, то по окончании вызова эти же множества содержатдескрипторы, на которых событие реально произошло (например, по сетиприбыли данные, которые могут быть считаны).Таким образом, работу с вызовом select() можно построить по следующей схеме (считаем, что номер слушающего сокета по-прежнему хранитсяв переменной ls; как хранить дескрипторы клиентских сокетов, читателюпредлагается решить самостоятельно):for(;;) { /* главный цикл */fd_set readfds;int max_d = ls;/* изначально полагаем, что максимальным являетсяномер слушающего сокета */FD_ZERO(&readfds); /* очищаем множество */FD_SET(ls, &readfds);/* вводим в множестводескриптор слушающего сокета */int fd;/* организуем цикл по сокетам клиентов */for(fd=/*дескриптор первого клиента*/ ;/*клиенты еще не исчерпаны?*/;fd=/*дескриптор следующего клиента*/) {/* здесь fd - очередной клиентский дескриптор *//* вносим его в множество */FD_SET(fd, &readfds);/* проверяем, не больше ли он,нежели текущий максимум */if(fd > max_d) max_d = fd;}int res = select(max_d+1, &readfds, NULL, NULL, NULL);if(res < 1) {/* обработка ошибки, происшедшей в select()’е */}if(FD_ISSET(ls, &readfds)) {17/* пришел новый запрос на соединение *//* здесь его необходимо принятьвызовом accept() и запомнитьдескриптор нового клиента */}/* теперь перебираем все клиентские дескрипторы */for(fd=/*дескриптор первого клиента*/ ;/*клиенты еще не исчерпаны?*/;fd=/*дескриптор следующего клиента*/)if(FD_ISSET(fd, &readfds)) {/* пришли данные от клиента с сокетом fd *//* читаем их вызовом \verb.read().

Характеристики

Тип файла
PDF-файл
Размер
373,54 Kb
Тип материала
Высшее учебное заведение

Список файлов книги

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6361
Авторов
на СтудИзбе
310
Средний доход
с одного платного файла
Обучение Подробнее