1.3.5 Порядок хранения байт
Существует два порядка хранения байтов в слове и двойном слове. Один из них называется порядком хоста (host byte order), другой - сетевым порядком (network byte order) хранения байтов. При указании IP-адреса и номера порта необходимо преобразовать число из порядка хоста в сетевой. Для этого используются функции htons (Host to Network Short) и htonl (Host to Network Long). Обратное преобразование выполняют функции ntohs и ntohl.
|
Таблица 1.6 |
Описание функции *nix |
Описание функции в Windows |
#include <netinet/in.h> |
#include <winsock2.h> |
unsigned short int htons( |
unsigned short int htons( |
unsigned short int hostshort |
unsigned short int hostshort |
); |
); |
unsigned long int htonl( |
unsigned long int htonl( |
unsigned long int hostlong |
unsigned long int hostlong |
); |
); |
unsigned short int ntohs( |
unsigned short int ntohs( |
unsigned short int netshort |
unsigned short int netshort |
); |
); |
unsigned long int ntohl( |
unsigned long int ntohl( |
unsigned long int netlong |
unsigned long int netlong |
); |
); |
1.3.6 Клиент-серверные приложения с установлением соединения (сервер)
socket
bind
listen
accept
send
recv
shutdown
close
Рисунок 1.1 Клиент-серверное
приложение с установлением
соединения (сервер)
Установка соединения на стороне сервера состоит из следующих этапов (см. рисунок 1.1). Сначала сокет создаётся и привязывается к локальному адресу. Если компьютер имеет несколько сетевых интерфейсов с различными IP-адресами, вы можете принимать соединения только с одного из них, передав его адрес функции bind. Если же вы готовы соединяться с клиентами через любой интерфейс, задайте в качестве адреса константу INADDR_ANY. Что касается номера порта, вы можете задать конкретный номер или 0 (в этом случае система сама выберет произвольный неиспользуемый в данный момент номер порта).
На следующем шаге создаётся очередь для ожидания запросов на соединение. При этом сокет переводится в режим ожидания запросов со стороны клиентов. Всё это выполняет функция listen.
Таблица 1.7
Описание в функции *nix |
Описание функции в Windows |
|
|
|
|
#include <sys/types.h> |
#include <winsock2.h> |
|
#include <sys/socket.h> |
||
|
||
int listen( |
int listen( |
|
int sockfd, |
SOCKET sockfd, |
|
int size |
int size |
|
); |
); |
Первый параметр - дескриптор сокета, а второй задаёт размер очереди запросов. Каждый раз, когда очередной клиент пытается соединиться с сервером, его запрос ставится в очередь, так как сервер может быть занят
обработкой других запросов. Если очередь заполнена, все последующие запросы будут игнорироваться. Когда сервер готов обслужить очередной запрос, он использует функцию accept.
|
Таблица 1.8 |
|
Описание в функции *nix |
Описание функции в Windows |
|
|
|
|
#include <sys/types.h> |
#include <winsock2.h> |
|
#include <sys/socket.h> |
||
|
||
int accept( |
SOCKET accept( |
|
int sockfd, |
SOCKET sockfd, |
|
void *addr, |
struct sockaddr *addr, |
|
int *addrlen |
int *addrlen |
|
); |
); |
Функция accept создаёт для общения с клиентом новый сокет и возвращает его дескриптор. Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в слушающем состоянии и может принимать другие соединения. В структуру, на которую ссылается addr, записывается адрес сокета клиента, который установил соединение с сервером. В переменную, адресуемую указателем addrlen, изначально записывается размер структуры; функция accept записывает туда длину, которая реально была использована. Если вас не интересует адрес клиента, вы можете просто передать NULL в качестве второго и третьего параметров.
Обратите внимание, что полученный от accept новый сокет связан с тем же самым адресом, что и слушающий сокет. Сначала это может показаться странным. Но дело в том, что адрес TCP-сокета не обязан быть уникальным в Internet-домене. Уникальными должны быть только соединения, для идентификации которых используются два адреса сокетов, между которыми происходит обмен данными.
1.3.7 Клиент-серверные приложения с установлением соединения (клиент)
socket
connect
send |
recv |
shutdown
close
На стороне клиента для установления соединения используется функция connect, которая имеет следующий прототип.
Рисунок 1.2 Клиент-серверное приложение с установлением соединения (клиент)
|
Таблица 1.9 |
|
Описание в функции *nix |
Описание функции в Windows |
|
|
|
|
#include <sys/types.h> |
#include <winsock2.h> |
|
#include <sys/socket.h> |
||
|
||
int connect( |
int connect( |
|
int sockfd, |
SOCKET sockfd, |
|
struct sockaddr *serv_addr, |
struct sockaddr * serv_addr, |
|
int addrlen |
int addrlen |
|
); |
); |
Здесь sockfd - сокет, который будет использоваться для обмена данными с сервером, serv_addr содержит указатель на структуру с адресом сервера, а addrlen - длину этой структуры. Обычно сокет не требуется
предварительно привязывать к локальному адресу, так как функция connect сделает это за вас, подобрав подходящий свободный порт. Вы можете принудительно назначить клиентскому сокету некоторый номер порта, используя bind перед вызовом connect. Делать это следует в случае, когда сервер соединяется только с клиентами, использующими определённый
порт. В остальных случаях проще и надёжнее предоставить системе выбрать порт за вас.
1.3.8 Обмен данными
После того как соединение установлено, можно начинать обмен данными. Для этого используются функции send и recv. В Unix для работы с сокетами можно использовать также файловые функции read и write, но они обладают меньшими возможностями, а кроме того не будут работать на других платформах (например, под Windows), поэтому ими лучше не пользоваться.
Функция send используется для отправки данных и имеет следующий прототип:
|
Таблица 1.10 |
|
Описание в функции *nix |
Описание функции в Windows |
|
|
|
|
#include <sys/types.h> |
#include <winsock2.h> |
|
#include <sys/socket.h> |
||
|
||
int send( |
int send( |
|
int sockfd, |
SOCKET sockfd, |
|
const char *msg, |
const char *msg, |
|
int len, |
int len, |
|
int flags |
int flags |
|
); |
); |
Здесь sockfd - это дескриптор сокета, через который мы отправляем данные, msg - указатель на буфер с данными, len - длина буфера в байтах, а flags - набор битовых флагов, управляющих работой функции (если флаги не используются, передайте функции 0). Вот некоторые из них (полный список можно найти в документации):
-MSG_OOB. Предписывает отправить данные как срочные (out of band data, OOB). Концепция срочных данных позволяет иметь два параллельных канала данных в одном соединении. Иногда это бывает удобно. Например, Telnet использует срочные данные для передачи команд типа Ctrl+C. В настоящее время использовать их не рекомендуется из-за проблем с совместимостью (существует два разных стандарта их использования, описанные в RFC793 и RFC1122). Безопаснее просто создать для срочных данных отдельное соединение.
-MSG_DONTROUTE. Запрещает маршрутизацию пакетов. Нижележащие транспортные слои могут проигнорировать этот флаг.
Функция send возвращает число байтов, которое на самом деле было отправлено (или -1 в случае ошибки, для *nix и SOCKET_ERROR в Windows). Это число может быть меньше указанного размера буфера. Если вы хотите