
- •3.2. Протокол доставки пользовательских дейтаграмм udp
- •5. Рабочее задание и указание к его выполнению
- •Цель работы
- •Подготовка к работе
- •Стек протоколов tcp/ip
- •3.1. Протокол межсетевого взаимодействия ip, формат ip пакета.
- •3.2. Протокол доставки пользовательских дейтаграмм udp.
- •3.3. Протокол надежной доставки сообщений tcp
- •Программирование по схеме “клиент-сервер” с использованием интерфейса Windows Sockets
- •Int wsaStartup(word wVersionRequested, lpwsadata lpWsaData);
- •Int wsaCleanup(void);
- •Int wsaGetLastError(void);
- •Socket socket(int af, int type, int protocol);
- •Int closesocket(socket sock);
- •Char far * inet_ntoa(struct in_addr in);
- •#Define h_addr h_addr_list[0]
- •Int bind(socket s, const struct sockaddr far *name, int namelen);
- •Int listen(socket sock, int backlog);
- •Socket accept(socket s, struct sockaddr far *addr, int far * addrlen);
- •Void ExitThread(uint fuExitCode);
- •Bool TerminateThread(handle hThread, dword dwExitCode);
- •Connect (socket s, const struct sockaddr *peer, int peer_len);
- •Передача и приём данных.
- •Int sendto(socket s,const void* buf, size_t len, int flags,
- •Int recvfrom(socket s,void* buf,size_t len, int flags,
- •Алгоритм построения клиента и сервера.
- •5. Рабочее задание и указание к его выполнению.
- •Содержание отчёта.
- •Вопросы для самопроверки.
- •8. Библиографический список
Int bind(socket s, const struct sockaddr far *name, int namelen);
При отсутствии ошибки возращаемое значение 0 иначе SOCKET_ERROR (для Windows) или -1 (для UNIX).
Параметр s – это дескриптор сокета. С помощью параметров name и namelen передаются порт и сетевой интерфейс. Обычно в качестве адреса задаётся константа INADDR_ANY. Это означает, что будет принято соединение, запрашиваемое по любому интерфейсу. Если хосту с несколькими сетевыми адресами нужно принимать соединения только по одному интерфейсу, то следует указать IP-адрес этого интерфейса. Как обычно namelen – длина структуры SOCKADDR_IN. Если ваше приложение использовало bind для привязки порта к сокету, то ни одно другое приложение не сможет прослушивать этот порт.
Пример вызова функции bind:
if (bind(srv_socket,(LPSOCKADDR)&srv_address, sizeof(srv_adress))==SOCKET_ERROR) { closesocket(srv_socket); MessageBox(NULL, “bind Error”, “Error”, MB_OK); return; } |
Создание канала связи.
Если передаются дейтаграммные сообщения при помощи протокола негарантированной доставки UDP, канал связи не нужен. Сразу после создания сокетов и их инициализации можно приступить к передаче данных. Но для передачи данных с использованием протокола TCP нужно создать канал связи.
Сторона сервера:
Прежде всего нужно переключить сокет в режим приёма для выполнения соединения с клиентом при помощи функции LISTEN:
Int listen(socket sock, int backlog);
Через параметр sock функции необходимо задать дескриптор сокета, который будет использован для создания канала. Параметр backlog задаёт максимальный размер очереди для ожидания соединения (можно указывать значения от 1 до 5). Очередь содержит запросы на установку соединений для каждой пары значений (адрес IP, порт). Если указать значение backlog , больше максимально допустимого, то система уменьшит его, не сообщив об ошибке.
Пример вызова функции listen:
if (listen(srv_socket, 1) == SOCKET_ERROR) { closesocket(srv_socket); MessageBox(NULL, “listen Error”, “Error”, MB_OK); return; } |
Далее необходимо выполнить ожидание соединения. Это можно выполнить двумя различными способами:
Первый способ заключается в циклическом вызове функции accept до тех пор, пока не будет установлено соединение. Затем можно будет приступать к обмену данными.
Функция accept имеет следующий прототип:
Socket accept(socket s, struct sockaddr far *addr, int far * addrlen);
Параметр s – это дескриптор прослушиваемого сокета. Функция возвращает адрес приложения на другом конце соединения в структуре SOCKADDR_IN, на которую указывает параметр addr. Целому числу, на которое указывает параметр addrlen, ядро присваивает значение, равное длине этой структуры. Часто нет необходимости знать адрес клиентского приложения, поэтому в качестве addr и addrlen будет передаваться NULL.
Возвращаемое значение в случае ошибки: INVALID_SOCKET (для Windows) или -1 (для UNIX). Если ошибки нет, то функция accept создает сокет с теми же параметрами, что и SOCKET s и возвращает дескриптор созданного сокета. Сокет возвращенный функцией accept связан и может немедленно использоваться для приема и передачи данных.
Так как при вызове accept выполнение программы прекращается до установления соединения целесообразно осуществлять вызов функции из параллельного треда (потока). Для Windows функция создания потока выглядит так:
HANDLE CreateThread( LPSECURITY_ATTRIBUTES LPSA; DWORD cbStack; LPTHREAD_START_ROUTINE lpStartAddr; LPVOID lpvThreadParm; DWORD fdwCreate; LPDWORD lpIDThread; ); |
Подробно о параметрах функции можно узнать в MSDN Library. Для выполнения данной лабораторной работы необходимыми параметрами являются lpStartAddr , lpvThreadParm и lpIDThread. Параметр lpStartAddr определяет адрес функции потока, с которой должен будет начать работу создаваемый поток. Вполне допустимо и даже полезно создавать несколько потоков, у которых в качестве входной точки используется адрес одной и той же стартовой функции. Параметр lpvThreadParm позволяет передавать функции потока какое-либо инициализирующее значение. Оно может представлять собой или просто 32-битное значение, или 32-битный указатель на структуру данных с дополнительной информацией. Последний параметр функции lpIDThread – это адрес переменной типа DWORD, в которой функция вернет идентификатор, приписанный системой новому потоку. В Windows 95 передача NULL вместо этого параметра даст ошибку. В Windows NT версии 4.0 и выше разрешено передавать NULL в параметре lpIDThread. Поэтому для корректной работы вашего приложения на различных платформах необходимо указать функции адрес, для помещения в него идентификатор потока. Для завершения потока необходимо воспользоваться одной из функций: