Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Unix (21.03.12) Сокеты.doc
Скачиваний:
8
Добавлен:
25.08.2019
Размер:
73.73 Кб
Скачать

Архитектура клиент-сервера. Bsd-сокеты.

Все программы, взаимодействующие через сокеты, делятся на два типа: клиенты и серверы. Клиент - такая программа, которая посылает запросы серверу на выполнение действий,потом сервер обрабатывает запросы и отправляет обратно клиенту. Клиент принимает результат обработки данных. Сервер умеет принимать запросы от других клиентов, обрабатывать их и отсылать результаты. Чаще всего серверы работают бесконечно долго, то есть работают в бесконечном цикле:принять, обработать, отослать, принять...Клиент не является программой, работающей вечно.

Примеры клиентов и серверов: apache (сервер), explorer (клиент). Ещё пример: отправляем письмо через почтовую программу outlook express - это клиент, а сервер, принимающий письмо и есть сервер. Когда мы используем outlook для чтения, все остается на своих местах. Очередной пример: Putty (клиент), SSH (сервер). Ещё пример: XWindows и программа Xcalc.

При взаимодействии программ через BSD сокеты каждая программа должна внутри ОС создать сокет, обладающий номером, который возвращается при создании. Он похож на номер потока ввода-вывода. Для того,чтобы соединить между собой два сокета, каждому сокету нужно назначить адрес. В зависимости от его типа они имеют разный вид: говоря об IP протокола - адрес - номер порта и адрес компьютера. Чтобы связать сокет с адресом сокета используется системный вызов bind(). Для Сервера нужно указать адрес сокета,а для клиента это необязательно. Чаще всего клиент использует уже установленное соединение. Чтобы сервер мог принимать запросы от клиентов, сервер должен создать специальную очередь запросов. Каждый раз, когда клиент посылает запрос серверу, этот запрос становится в очередь. При этом клиент ждет, пока сервер не вытащит запрос из этой очереди запросов, как только сервер вытаскивает запрос из очереди, клиент продолжает работу. То есть сервер должен создать сокет,связать сокет с его адресом, создать очередь запросов, в цикле вынимать запрос из очереди, получить данные от клиента, и обрабатывать их, а результат обработки послать клиенту.

Как устроен клиент. Клиент тоже должен создать сокет, дальше сделать bind, а может и не делать. Потом поставить запрос в очередь запросов. Если очередь запросов пуста, то клиент продолжает работу, а если нет, то клиент находится в состоянии ожидания до тех пор, пока его запрос не будет вынут из очереди запросов. После того,как запрос вынут, клиент должен послать данные, дожидаться ответа от сервера после обработки и закончить свою работу.

Итак, ещё ра по порядку:

Сервер должен: - создать сокет - связать сокет с адресом сокета, используя bind() - создать очередь запросов - в цикле вынимать запросы из очереди запросов - обрабатывать их - посылать результаты обработки клиенту

Клиент должен: - создать сокет - может делать bind для связи этого сокета с каким-либо адресом или не делать это - поставить запрос в очередь запросов сервера - если очередь запросов пуста, клиент продолжит работу, если не пуста - клиент перейдет в состояние ожидания, до тех пор, пока его запрос не будет вынут из очереди запросов - после этого клиент должен послать данные, касающиеся запроса серверу и дождаться ответа сервера - после этого можно закончить работу

Системные вызовы для создания и работы с сокетами.

Что делает сервер?

Первый системный вызов - socket().

socket(<домен, к которому относится socket>, <тип сокета>, <номер протокола>)

Обладает тремя параметрами:

1) домен, к которому относится сокет (указывается с помощью символьной константы PF_UNIX PF_LOCAL, PF_INET) . Мы будет изучать два: первый — IP-сокеты или интернет-сокеты, второй - Unix- или локальные сокеты. Первые служат для связи программ через интернет, вторые - для связи программ на одном компьютере.

2)типа сокета: SOCK_STREAM ориентирован на соединение, SOCK_DGRAM. Первый обладает следующими свойствами: пакеты не теряются, приходят в том порядке,в котором были посланы, сообщения никогда не дублируются. В SOCK_DGRAM все сообщения передаются независимо.

      1. номер протокола - ранее полагали, что это важно, но на данный момент используются только первые два (т.к. их достаточно для определения сокета), в этом месте всегда ставится 0.

После выполнения всех действий, функция socket возвращает номер сокета, который можно использовать в операциях ввода-вывода (то есть который может быть использован в read или write) .

bind() связывает между собой адрес сокета и сокет.

bind(<номер сокета>, <структура, соответствующая домену>, <длина этой структуры>)

Для адреса используется специальная структура данных: sockaddr. Эта структура данных обладает двумя полями: длина и домен,для которого этот адрес сокета указывается. Остальные поля структуры зависят от того,к какому домену относится этот сокет. Для IP-сокетов это одни поля, для юниксовых - другие, там указываются имя файла. Соответственно, чтобы задать адрес сокета для айпи сокетов надо использовать структуру данных sockaddr_in, а для юникосовых - sockaddr-un. Всегда используется модификация sockaddr,в чистом виде она не используется. Чтобы посмотреть,какие поля содержатся в структуре sockaddr_in 齏è sockaddr-un надо использвать man 4 inet или man 4 unix.

Структура собственной персоной:

struct sockaddr_un {                                     u_char  sun_len;                                     u_char  sun_family;                                     char      sun_path[104];                     };

sun_len - длина структуры, sun_family, sun_path. Bind создает объект спец типа с именем, указанным в sun_path. Если объект уже существует, bind() вернет ошибку. Обычно sun_path - системный вызов bind создает специальный файловый объект типа сокет с именем, указанным в sun_path. Если такой уже существует - bind вернет ошибку. Перед вызовом bind лучше удалить объект с таким именем (в случае уверенности в своих действиях). Обычно здесь указывают полное имя файлового объекта (начиная со /). Если указывать относительное - чтобы клиент мог найти сервер, и клиент, и сервер должны запускаться из одной и той же директории. Путь, указанный в sun_path должен обязательно оканчиваться нулевым символом.

Третий параметр bind - длина области данных,в которой записан адрес сокета.

Очередь запросов создают с помощью вызова listen().

listen(<номер сокета, для которого создается очередь запросов>, <длина этой очереди>);

Мы тем самым сообщаем ОС,что программа является сервером,а не клиентом. У вызова два параметра: номер сокета, для которого создается очередь, и длина этой очереди. Большинство ОС не использует число запросов напрямую,а использует его как приближение. В частности, указав число больше 5, очередь будет практически бесконечной.

Дальше мы должны запросы вынимать с помощью системного вызова accept().

accept(<номер сокета>, <адрес буфера>, <входной/выходной параметр, на входе - длина буфера, на выходе - сколько байтов было реально записано>);

Он ждет, пока в очереди появится запрос, и как только он появляется, он возвращает управление серверу. У вызова три параметра: номер сокета, адрес буфера, в который запишется адрес сокета клиента, входной/выходной : длина буфера/сколько реально байт было записано. Третьим параметром передается не число,а указатель на это число. Обычно accept выполняется в цикле. Он возвращает присоединенный сокет. Это такой сокет, который связан непосредственно с тем клиентом, запрос от которого мы только что вынули, то есть он предназначается для общения между собой клиента и сервера.

(Это такой сокет, который связан непосредственно с тем клиентом, чей запрос мы только что вынули. Используется для индивидуального общения с клиентом).

Чтобы передать данные, используется вызов read()

read(<номер присоединенного сокета>);

в котором параметр - номер присоединенного сокета. Мы с его помощью можем прочитать данные с клиента.

После всего этого мы можем послать результаты обработки с помощью write().

write(<номер присоединенного сокета>);

Для разрыва соединения надо использовать вызов close().

close(<номер присоединенного сокета>);

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]