Как было показано, отсутствие имен у каналов делает их недоступными для независимых процессов. FIFO имеют имена. Другие средства межпроцессного взаимодействия требуют дополнительных соглашений по именам и идентификаторам. Множество возможных имен объектов конкретного типа межпроцессного взаимодействия называется пространством имен ( name space). Имена являются важным компонентом системы межпроцессного взаимодействия для всех объектов, кроме каналов, т. к. позволяют процессам получить доступ к общему объекту. Именем FIFO, является имя файла именованного канала. Используя условное имя созданного FIFO два процесса могут обращаться к этому объекту для обмена данными.
Для таких объектов IPC, как очереди сообщений, семафоры и разделяемая память, процесс назначения имени более сложен, чем просто указание имени файла. Имя для этих объектов называется ключом (key) и генерируется функцией ftok(3c) из двух компонентов – имени файла и идентификатора проекта:
# include <sys/types. h>
# include <sys/ipc. h>
key –t ftok (char *filename, char proj);
В качестве filename можно использовать имя некоторого файла, известное взаимодействующим процессам, например, имя программы-сервера. Главное, чтобы этот файл существовал на момент создания ключа. Нежелательно использовать имя файла, который создается и удаляется в процессе работы распределенного приложения, т. к. при генерации ключа используется номер inode файла. Вновь созданный файл может иметь другой inode и впоследствии процесс, желающий иметь доступ к объекту, получит неверный ключ.
Пространство имен позволяет создать и совместно использовать IPC неродственным объектам.
Каждое IPC имеет свой уникальный дескриптор (идентификатор), используемый ядром ОС для работы с объектом. Уникальность дескриптора обеспечивается уникальностью дескрипторов для каждого из типов объектов (очереди сообщений, семафоры и разделяемая память).
Идентификация объектов выполняется в соответствии с табл.
Таблица . Идентификация объектов IPC
Объект IPC | Пространство имен | Дескриптор |
Канал | – | Файловый дескриптор |
FIFO | Имя файла | Файловый дескриптор |
Очередь сообщений | Ключ | Идентификатор |
Семафор | Ключ | Идентификатор |
Разделяемая память | Ключ | Идентификатор |
4. СООБЩЕНИЯ
Внутренний, связанный список процессов в порядке их поступления, который в виде очереди хранится в адресном пространстве ядра. Для каждой очереди ядро создает заголовок msgid_ds, где содержится информация оправах доступа к очереди, ее текущем состоянии на первое и последнее сообщения. Каждый элемент очереди является отдельным сообщением.
5. СЕМАФОРЫ
Используются для синхронизации доступа нескольких процессов к разделяемым ресурсам. Разрешают или запрещают доступ к разделяемому ресурсу.
Семафоры являются системным ресурсом, действия над которыми производятся через интерфейс системных вызовов.
Семафоры хранятся в адресном пространстве ядра.
6. РАЗДЕЛЯЕМАЯ ПАМЯТЬ
Этот механизм позволяет двум процессам использовать для обмена общий участок памяти, куда информация будет попадать и считываться минуя ядро. Задача кооперативного использования памяти, при этом, решается при помощи семафоров.
7. СОКЕТЫ
Для реализации межпроцессорного взаимодействия между процессами, которые выполняются на разных компьютерах сети которые могут иметь различные схемы адресации объектов, их расположение, протоколы передачи данных и т. д.
Для коммутационного узла, обеспечивающего прием и передачу данных для объекта (процесса) предложен специальный объект – сокет (socket), который создается в рамках коммутационного домена (communication domain) и адресуется своим дескриптором.
Сокет – виртуальный объект. Он существует, пока в нем есть необходимость.
Реализованы основные виды сокетов:
– сокет датаграмм (datagram socket) – осуществляет теоретически ненадежную, несвязную передачу пакетов;
– сокет потока (stream socket) – осуществляет надежную передачу потока байтов, без сохранения границ сообщений. Используется для передачи экстренных сообщений;
– сокет пактов (packet socket) – выполняет надежную передачу потока данных без дублирования с предварительным установлением связи. При использовании, сохраняются границы сообщений;
– сокет низкого уровня (raw socket) – такой сокет позволяет осуществить непосредственный доступ к коммуникационному протоколу.
Другие интерфейсы к коммуникационным протоколам
В UNIX в качестве интерфейса к TCP/IP в основном используются следующие API:
1. сокеты Беркли;
2. транспортный интерфейс XTI (X/Open Transport Interface);
Интерфейс XTI является расширением к транспортному протоколу TLI(XTI/TLI)-Transport Layer Interface. Он задумывался как интерфейс, не зависящий от протокола, так как он легко поддерживал новые протоколы OSI, которые, как считалось, придут на смену TCP/IP. Имена функцийTLI похожи на используемые в API сокетов, но начинаются с t_. Но их семантика кардинально отличается в ряде случаев. Этот протокол используется в системах фирмы NOVELL, а также поставляются в OC UNIX. Протоколы XTI называется интерфейсом наряду с интерфейсом сокетов, так как это всего лишь доступ к стеку TCP/IP, так как коммуникационные протоколы реализуются стеком протоколов, также имеет значение, какой API использует клиент. Приложение с использованием сокетов, может обмениваться данными с приложением на базе XTI. В системах SVR4 оба интерфейса обычно реализуются в виде библиотек. Достоинством XTI/TLI является возможность с его помощью добавить новый протокол, не имея доступа к ядру, причём достаточно просто.
Тем не менее есть примеры реализации с помощью сокетов простого, не зависящего от протокола сервера времени дня, который поддерживает IP версий 4 и 6, и сокеты в адресном домене UNIX. Существует спор, какие из интерфейсов более производительны, но они одинаковы.
Большое преимущество сокетов –переносимость - на MsWin. Сокеты проще используются, и их рекомендуется преимущественно использовать SVR4 и SVR6-протоколы IP, позволяющие пересылать в пакете 4 или 6 байтов.
Solaris-это SVR4 фирмы SUN.
TCP как потоковый протокол
Данные доставляются получателю в виде потока, в котором нет понятия сообщения или границы сообщения. В этом отношении чтение данных похоже на чтение из последовательного порта - заранее неизвестно, сколько байтов будет возвращено после выполнения функции чтения. Для TCP –приложения нет понятия «пакет», и хотя данные передаются в IP-пакетах, размер пакета не связан напрямую с количеством данных, переданных TCP при вызове send. У принимающего приложения нет надёжного способа определения, как именно данные распределены по пакетам, так как между соседними вызовами recv может прийти несколько пакетов. Если один пакет потерян, а последующие пришли нормально, TCP “задерживает поступившие данные”, пока не будет повторно передан и корректно принят пропавший пакет. TCP следит за количеством байтов, посланных и подтвержденных, но не за их распределением по пакетам. Если пользоваться для чтения данных стандартной библиотечной функцией fgets, то она сама будет разбивать поток байтов на строки. Самый простой случай-это сообщение фиксированной длины. Тогда нужно прочесть заранее известное число байтов из потока. Для этого недостаточно выполнить простое однократное чтение:
Recv(s, msg, size of(msg),o);
Так как при этом можно получить меньше, чем size of(msg) байт
Функция readn
1. in readn(SOCKET fd, char*bp, size_t len)
2.
3. {
4. int ent;
5. int rc;
6. ent=len;
7. while(ent>0)
8. {
9. rc=recv(fd, bp, ent,0);
10. if(rc<0) /’’ошибка чтения?*/
11. {
12. if(errno==ENTER)/*Вызов прерван?*/
13. continue;/*повторить чтение*/
14. return-1/*вернуть код ошибки*/
15. }
16. if(rc==0)/*конец файла*?/
17. return len-ent/*вернуть неполный счётчик*/
18. bp+=rc
19. ent-=rc;
20. }
21. return len;
22. }
Функция readn не возвращает управление, пока не будет прочитано len байт или не получен конец файла, или не возникнет ошибка. Её прототип выглядит следующим образом :
# include <etcp. h>
int readn(SOCKET s, char*buf, size_tlen);
возвращаемое значение - число прочитанных байтов или –1 в случае ошибки.
Оператор if
If (errno==ENTER)
Continue;
(Errno-глобальная переменная, ENTER - прерванный вызов функции) возобновляет выполнение вызова recv, если он прерван сигналом.
В случае сообщений переменной длины есть два метода разделения записей:
1. с помощью специальных маркеров с использованием fgets для разбиения потока на строки, например, с помощью разделителя-символа новой строки/. Тогда, если оно применяется в теле, то нужно здесь заменить его на //. На приёмной стороне происходит обратный процесс.
2. Каждое сообщение снабжается заголовком, содержащим, как минимум длину следующего за ним тела.
|
ДРУГИЕ ДАННЫЕ ЗАГОЛОВКА |
ДАННЫЕ ПЕРЕМЕННОЙ ДЛИНЫ |
В процедуре fgets происходит считывание символов из потока в буфер пока не встретится символ/.
Принимающее приложение читает сообщение в два приёма: заголовок фиксированной длины и из него извлекается переменная длина тела сообщения, а затем самое тело.
1. int readvrec(SOCKET fd, char*bp, size_t len),где SOCKET-тип данных, t-длина буфера.
2. {
3. uint32_t reclen;
4. Int rc;
5. /*прочитать длину записи*/
6. rc=readn(fd,(char*)&reclen, size of(u_int32_t));
7. if(rc!=sizeof(u_int32_t))
8. return rc<0?-1:0;
9. reclen=ntohl(reclen);/*сетевой в машинный*/
10. if(reclen>len)
11. {
12. /*
13. *Не хватает места в буфере для размещения
14. *данных – отбросить их и вернуть код ошибки
15. */
16. while(reclen>0)
17. {
18. rc=readn(fd, bp, len);
19. if(rc!=len)
20. returnrc<0?-1:0;
21. reclen==len;/*reclen=reclen-len*/
22. if(reclen<len)
23. len=reclen;
24. }
25. set_errno(EMSGSIZE);
26. return-1;
27. }
28. /*прочитать саму запись*/
29. rc=readn(fd, bp, reclen);
30. if(rc!=reclen)
31. return rc<0?-1:0;
32. return rc;
33. }
Чтение длины записи
Длина записи считывается в переменную reclen. Функция readvrec возвращает 0(конец файла), если число байтов, прочитанных readn не точно совпадает с размером целого или –1 в случае ошибки.
Размер записи преобразуется из сетевого порядка в машинный.
Проверка того, поместится ли в буфер запись
Проверка того, поместится ли в буфер запись, достаточна ли длина буфера, предоставленного вызывающей программой, для размещения в ней записи. Если места недостаточно, то данные считываются в буфер частями по len байт, то есть по сути отбрасываются. Изъяв из потока отбрасываемые данные, функция присваивает переменной errno значение EMSGSIZE и возвращает –1.
Считывание записи
Считывается сама запись, readvrec возвращает –1,0 или reclen в зависимости от того, вернула ли readn код ошибки, неполный счётчик или нормальное значение.
Функция strlen
Функция strlen возвращает длину строки sl(число символов в строке sl до нулевого символа в строке, обозначающего её конец).
Функции ntohe и ntohs
Преобразуют целое число из сетевого порядка в машинный. В современных компьютерах целые числа хранятся по-разному в зависимости от архитектуры процессора.
Например, число $ может храниться в памяти:
1. big endian(Motorola)тупоконечные
2. little endian(INTEL)остроконечные
Терминология введена Коэном в 1981г.
Определить формат, применяемый в конкретной машине, можно с помощью следующей программы:
1. #include<stdio. n>
2. #include<sys/types. n>
3. #include”etcp. h”
4. int main(void)
5. {
6. u_int32_t x=0x;/**/
7. unsigned char xp=(char)&x;
8. printf(“%0x%0x%0x%0x)n”;
9. xp[0],xp[1],xp[2],xp[3];
10. exit(0);
11. }
Формат хранения байтов важен, так как машины с разным принципом хранения часто общаются друг с другом по протоколам TCP/IP. Такая информация, как адреса отправления и назначения, номера портов, длина дейтограмм, размеры окон, представляются в виде целых чисел, необходимо их одинаково интерпретировать.
Чтобы обеспечить взаимодействие компьютеров с разными архитектурами, величины, относящиеся к протоколам, передаются в сетевом порядке, который по определению является тупоконечным. Сетевые адреса, номера портов, другие данные, представленные в заголовках, задаются программистом, и их нужно преобразовывать в сетевой порядок.
Функции htone и htons возвращают 32 и 16-разрядные числа в сетевом порядке. Функции ntone и ntons возвращают целое число в машинном порядке. На любой машине эти функции следует применять всегда, остальное делает система.
#include<netinet/in. h>
|
uint16_t htons(uint16_t host16);
|
uint16_t ntohs(uint16_t network16)
Функции разрешения имён
Gethostbyname и getservbyname
Преобразование имён хостов
Эти функции возвращают значение, представленные в сетевом порядке. Фрагмент программы:
Struct servent*sp;
Struct sockaddr_in*sap;
Sp=getservbyname(name, protocol);
Sap->sin_port=htons(sp->ss_port);
Приведёт к ошибке, если исполняется не на тупоконечной машине.
Аккуратное размыкание соединение
В работе TCP – соединения есть три фазы:
1. Установление соединения
2. Передача данных
3. Разрыв соединения
Так как TCP –соединения полудуплексные, потоки данных в разных направлениях не зависят друг от друга.
Например, клиент может соединиться с сервером, отправить серию запросов и закрыть свою половину соединения, предоставив серверу информацию о том, что больше запросов не будет. Сервер может продолжать посылать данные после того, как клиент прекратил отправлять запросы.
С другой стороны сервер может посылать в ответ сколько угодно данных, и клиент не знает заранее, когда ответ закончится, и тогда сервер закроет свой конец соединения, сигнализируя о конце передачи. После закрытия сервером своего конца соединения, TCP завершает фазу разрыва, чем сообщает клиенту о прекращении передачи данных.
Вызов shutdown
Используется как вызов close, но при этом передаётся дополнительный параметр, означающий, какую сторону закрыть.
#include<sys socket. h>
#int shutdown(int s, int how)
Возвращаемые значения:0-нормально, 1-ошибка.
How=0-закрывается принимающая сторона соединения. В UNIX все полученные после выполнения данные уничтожаются.
How=1-закрывается отправляющая сторона соединения. В сокете делается отметка, что данные посылаться больше не будут.
How=2-закрываются обе стороны соединения.
При выполнении shutdown не закрывает сокет по настоящему, так как ресурсы не освобождаются.
Алгоритм Найгла
Алгоритм Найгла предложен в 1984г. и влияет на стратегию отправки TCP-регулирует количество передаваемых по сети данных, достигая наиболее эффективного использования полосы пропускания. TCP посылает данные блоками, размер которых равен MSS-максимальный размер сегмента. При установлении соединения TCP может указать максимальный размер сегмента(MSS), но не больше, чем разрешено окном передачи. Для того, чтобы избежать перегрузки сети и затора (при превышении памяти маршрутизатора дейтограммы отбрасываются), а повторные передачи ещё больше загрузят сеть, используют два окна – окно передачи и окно перегрузки. Окно передачи, объявляемое TCP на клиентской стороне, предохраняет от переполнения его буферов. Окно перегрузки на серверной стороне не даёт превысить пропускную способность сети. Если ограничить объём передачи минимальным из этих окон, то оба требования управления потоком будут удовлетворены. Это алгоритм –медленный старт. Второй алгоритм –Найгла - требует, чтобы в TCP не было более одного неподтверждённого маленького сегмента менее MSS. Цель алгоритма Найгла - не дать TCP последовательностью мелких сегментов, а сохраняет в своих буферах небольшие блоки данных, пока не получит подтверждение на предыдущий маленький сегмент. При каждом вызове процедуры вывода TCP вычисляет объём данных, которые можно послать. Это минимальное значение количества данных в буфере передачи, размера окон передачи и перегрузки и MSS. Данные отправляются при выполнении хотя бы одного из следующих условий:
1. можно послать полный сегмент размером MSS
2. соединение простаивает и можно освободить буфер передачи
3. алгоритм Найгла отключён и можно опустошить буфер передачи
4. есть срочные данные для отправки
5. есть маленький сегмент, но его отправка уже задержана на длительный срок
6. окно приёма, объявленное на приёмной стороне открыть не менее чем наполовину
7. необходимо повторно передать сегмент
8. требуется послать ACK на принятые данные
Алгоритм Найгла нужно иметь возможность отключить, так как он уменьшает производительность const int on=1;
Setsockopt(s, IPPROTO_TCP, TCP_NODELAY,&on, sizeof(on));
Программная реализация архитектуры КЛИЕНТ – СЕРВЕР
Роль программ клиента и сервера бывает разной. Иногда они равноправные.
В случае с ТСР/IР различие четкое. Сервер обслуживает порт, чтобы обнаружить входящие соединения или ИДР-дейтограммы от одного или нескольких клиентов с другой стороны клиент – это тот, кто первым начал диалог.
Исследования, разработка и проверка программ должна производиться на одной машине, т. к. исключаются сетевые задержки, пакеты не пропадают и приходят в правильном порядке.
Элементы АРI сокетов

Рисунок 1 – основные вызовы сокетов для клиентов
Адрес удаленного Хоста задается с помощью структуры sockaddr_in, которая передается функции connect. Первый шаг – это получение сокета для логического соединения с помощью системного вызова socket
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
Socket socket (int domain, int type, int protocol);
Возвращаемое значение: дескрипторов в случае успеха; -1 (UNIX) или INVALID_SOCKET(Windows)-ошибка АРI сокетов не зависит от протокола и может поддерживать разные адресные домены. Параметр domain—константа, указывающая, какой домен нужен сокету. Чаще используются домены AF_INET (Internet) и AF_LOCAL(или AF_UNIX). AF_LOCAL применяется для межпроцесного взаимодействия (IC) на одной машине. Вместо AF можно использовать PF_*. Параметр type задает тип создаваемого сокета.
Возможные значения сокетов:
1 SOCK_STREAM – обеспечивает нужный дуплексный протокол на основе установления логического соединения (ТСР)
2 SOCK_DGRAM – обеспечивает ненадежный сервис доставки дейтограмм (ИДР)
3 SOCK_RAW – предоставляет доступ и некоторым дейтограмм на уровне IP, используется, например, для просмотра всех ICMP—сообщений
Параметр protocol показывает, какой протокол следует использовать с данным сокетом. В качестве TCP/IP он обычно неявно определяется типом сокета, поэтому в качестве значения задают 0.
Для самого простого TCP-клиента требуется еще один вызов АРI сокетов, обеспечивающий установление соединения:
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
int connect (SOCKET_s, const struct sockaddr*peer, int peer_len);
Возвращаемое значение: 0-нормально, -1 (UNIX) или не 0 – ошибка
s-параметр, который является дескриптором сокета, который вернул системный вызов SOCKET.
Параметр peer указывает на структуру, в которой хранится адрес удаленного хоста и дополнительная информация.
Для домена AF_INET –это структура типа sockaddr_in. Параметр peer_len содержит размер структуры в байтах, на которую указывает peer.
После установления соединения можно передавать данные. В ОС UNIX нужно обратиться к системным вызовам read и write и передать или дескриптор сокета.
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
int recv (socket s, woid*buf, suze_tlen, int flags);
int send (socket s, const void*buf, size_tlen, int flags);
Возвращаемое значение: число принять на переданных байтов в случае успеха или –1 в случае ошибки.
Параметры s, buf и len аналогичные используемым для read и write. Flags зависит от системы, но и UNIX и windows поддерживают следующие флаги:
1 MSG_OOB – Следует послать или принять срочные данные
2 MSG_PEEK – Используется для просмотра поступивших данных без их удаления из приемного буфера. После возврата из системного вызова данные еще могут быть получены при последующем вызове read или recv;
3 MSG_DONTROUTEK – сообщает ядру, что не надо выполнять обычный алгоритм маршрутизации, как правило, используется программами маршрутизации и для диагностических целей.
При работе с протоколом TCP больше ничего не нужно. При работе с ИДР нужны системные вызовы recvfrom и sendto; они отличаются тем, что при отправлении дайтограмм позволяют задать адрес назначения, а при приеме -- получить адрес источника.
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
int recvfrom (sockets, const void*size_t len, int flags, const struct sockaddr*to, int tolen);
Возвращаемое значение: число принятых и переданных байтов в случае успеха или –1 при ошибке. Параметры s, buf, Len, flags, --такие же, как в вызовах recv и send. Параметр from в вызове recvfrom указывает на структуру, в которую ядро помещает адрес источника пришедшей дейтограммы. Длина этого адреса хранится в целом числе, на которое указывает параметр fromLen (указатель на целое).
Параметр to в вызове sendto указывает на адрес структуры, содержащий адреса назначения дейтограммы, а параметр tolen – длина этого адреса. To – это целое, а не указатель.
1.2 Разработка программ в архитектуре “клиент-сервер”
Большинство прикладных программ является не единым программным модулем, а набором взаимодействующих между собой подпрограмм, одни из которых являются модулями ОС, а могли вообще принадлежать другим разработчикам – их надо смело поставлять и устанавливать, например, из Интернет. В прикладной программе можно выделить два уровня:
1 Нижний, отвечающий за методы хранения доступа, разделения данных,-- поставляемый
2 Верхний, отвечающий за логику обработки данных и интерфейс пользователя –собой.
Первый уровень часто связан с базами данных и доступом к ним, и для работы первого уровня часто требовалося наличие высокопроизводительной системы.
Второй не требовал наличия высокопроизводительной системы вычислений и включая в себе собственно алгоритм логику и весь интерфейс, созданный разработчиком программы. Так сложилось понятие приложения, построенного на архитектуре «клиент - сервер».
Первый уровень, сервер, включает методы, связанные с доступом данных; их реализует сервер БД из соответствующей СУБД в комплекте с драйверами доступа к телу.
Второй уровень, клиент, взаимодействует, с одной стороны, с сервером, получая от него данные, а с другой стороны – с пользователем, ресурсами приложения и ОС, осуществляя обработку данных и отображение результатов. Результаты обработки клиент опять-таки может сохранить в обработку данных и отображение результатов. Результаты обработки клиент опять-таки может сохранить в БД, используя функции серверной части. Многие известные фирмы стали предлагать стандартные интерфейсы для доступа к создаваемым им СУБД. Изменилась и структура систем программирования; они стали ориентироваться на создание в архитектуре, клиент - сервер. Предлагаемые или средства поставляются в составе систем программирования и поддерживают возможность работы с широким диапазоном известных серверов данных через один или несколько доступных интерфейсов обмена данными. Разработчик прикладной программы выбирает одно из доступных средств и возможный тип сервера. Тогда задача сводится только к созданию клиентской части приложения, построенной на основе выбранного интерфейса. В дальнейшем использовать приложение можно только в комплексе. Интерфейс обмена данными входит в систему программирования и свободно распространяется.
Серверная часть распространяется только по лицензиям, надо лицензировать средства создания и отладки БД, но результаты работы могут распространяться. Либо в случае мощных серверов, лицензия нужна на и распространение серверной части приложение. В этом случае конечный пользователь получает комплекс программы, продуктов от множества разработчиков. Развитием архитектуры программирования «клиент – сервер» явилась трехуровневая архитектура. Клиентская часть разделилась на две составляющие: сервер приложений и «тонкий клиент, обеспечивающий интерфейс доступ к результатам обработки. Серверная часть, сервер без данных, осталась без изменений. Взаимодействие между сервером приложений и тонким клиентом должно быть тесным, а, с другой стороны, должен обмениваться данными по протоколам интернет, которые стандартизованы, например, COM/DCOM, CORBA и другие (COMMON Object Reguest Broklr Architecture)
Пример простого ТСР – клента
1 #include<sys/types. h>
2 #include<sys/socket. h>
3 #include<netinet/in. h>
4 #include<arpa/inet. h>
5 #include<stdio. h>
6 int main (void)
7 {
8 struct sockaddr_in peer;
9 int d;
10 int rc;
11 char buf [ 1 ];
12 peer. sin_family = AF_INET;
13 peer. sin_pott = Atons( 7500 );
14 peer. sin_addr. s addr = inet_addr(“127.0.0.1”);
15 S = socket (AF_INET, SOCK_STREAM, 0 );
16 If ( s < 0)
17 {
18 perror( «ошибка вызова sockett»);
19 exit ( 1 );
20 }
21 rc=connect ( s, ( struct sockaddr * )&peer, sizeof( peer) );
22 if ( rc<=0 )
23 {
24 perror ( «ошибка вызова connect» );
25 exit ( 1 );
26 }
27 rc = send ( s, “1”, 1, 0 );
28 if ( rc <= 0 )
29 {
30 perror («ошибка вызова send» );
31 exit ( 1 );
32 }
33 rc = recv( s, buf, 1, 0 );
34 if ( rc <= 0 )
35 perror( «ошибка вызова reccv» );
36 else
37 printf ( “%\n”, buf [ 0 ];
38 exit( 0 );
39 }
Подготовка адреса сервера:
12 – 14 Заполнение структуры sockaddr_in путем записи в ее поля номера порта (7500) и адреса 127.0.0.1 – это возвратный адрес, который означает, что сервер находится на той же машине, что и клиент.
Получение сокета и соединение с сервером:
15 – 20 Получение сокета типа SOCK_STREAM, т. к. используется потоковый протокол ТСР.
21 – 26 Установка соединения с сервером путем обращения к системному вызову connect. Это вызов нужен, чтобы сообщить ядру адрес сервера.
Отправка и получение одного байта
27 – 38 Сначала один байт серверу, затем из сокета читается один байт и записывается в стандартный вывод. Сеанс завершается.
Перед тестированием клиента следует подготовить сервер.
Сервер должен быть готов к установлению соединений с клиентами. Для этого он должен прослуживать известный ему порт с помощью системного вызова listen.
![]() |
Рисунок – Основные вызовы API сокетов для сервера.
Но предварительно необходимо привязать адрес интерфейса и номер к прослеживающему сокету. Для этого предназначен вызов bind.
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
int bind( socket s, const sockaddr*name, int namelen );
Возвращаемое значение:0 – нормально, -1 или SOCKET_ERROR (Windows) – ошибка
Параметр s – это дескриптор просуживающего сокета. С помощью параметров name и namelen передаются порт и сетевой интерфейс, которые нужно прослеживать. Обычно в качестве адреса задается константа INADDR_ANY. Это означает, что будет прямое соединение, запрашиваемое по любому интерфейсу. Если хосту с несколькими сетевыми адресами нужно принимать соединение только по одному интерфейсу, нужно указать IP – адрес этого интерфейса. Через namelen обозначается длина структуры sockaddr_in.
После привязки локального адреса к сокету нужно перевести сокет в режим прослушивания входящих соединений с помощью системного вывода listen. Его задача – пометить сокет как прислуживающий. Когда хосту поступает запрос на установление, ядро ищет для которого адрес назначения и номер порта соответствуют указанным в запросе.
#include <sys/socket. h>/*UNIX*/
#include <winsock2.h>/*windows*/
int listen( SOCKET s, int backlog );
0 – нормально; -1(UNIX) или SOCKET_ERROR (Windows) – ошибка.
Параметр – это дескриптор сокета, который нужно перевести в режим прослушивания. Параметр backlog – это максимальное число ожидающих, но еще не принятых соединений. Это не максимальное число одновременных соединений с данным портом, а максимальное число частично установленных соединений с данным портом, ожидающих в очереди, пока приложение их примет традиционно значение параметра backlog не более пяти соединений, ожидающих в очереди, но в современных реализациях, которые должны поддерживать приложение с высокой нагрузкой, оно может быть намного больше. Если не изучить документацию по конкретной системе и задать s больше допустимого, то системе уменьшит его, не сообщив об ошибке.
Вызов accept служит для приема соединения, ожидающего во входной очереди. После того, как соединение принято, его можно использовать для передачи данных, например, с помощью вызовов recv и send. В случае успеха accept возвращает дескриптор нового сокета, по которому будет происходить обмен данными. Номер локального порта для этого сокета такой же, как и для прослушивающего сокета. Адрес интерфейса, на котором поступил запрос о соединении, называется локальным. Адрес и номер порта клиента считаются удаленными. Оба сокета имеют один и тот же номер локального порта. Это справедливо, т. к. ТСР – соединение полностью определяется четырьмя параметрами – локальным адресом, локальным портом, удаленным адресом, удаленным портом. Т. к. удаленный адрес и порт для этих двух сокетов различны, то ядро может отличить их друг от друга.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


ДЛИНА ЗАПИСИ
