Сокеты

На сегодняшний день практически все распределённые системы работают с использованием сокетов.

Создание сокета

#include <sys/types.h>
#include <sys/socket.h>
 
int socket(int domain, int type, int protocol);
  • domain - коммуникационный домен:
    • AF_UNIX
    • AF_INET
  • type - тип сокета:
    • SOCK_STREAM - виртуальный канал (логическое соединение между взаимодействующими сокетами и соблюдение порядка отправки и приёма сообщений от начала до конца работы канала)
    • SOCK_DGRAM - датаграммы (нет предварительной установки соединения. Не гарантируется порядок отправки и приёма сообщений. Не гарантируется доставка сообщений)
  • protocol - протокол:
    • 0 - автоматический выбор протокола
    • IPPROTO_TCP - протокол TCP(AF_NET)
    • IPPROTO_UDP - протокол UDP(AF_NET)

Возвращает файловый дескриптор для нового сокета. -1 и errno в случае ошибки.

Тип коммуникационного домена определяет способ адресации

Для датаграмм также доступен TCP.

Связывание

Окей. Сокет создали. Как его теперь найти?

Когда сокет создан socket(), он существует в пространстве имён, но не имеет связанного адреса. bind() привызяывает адрес из addr к сокету.

#include <sys/tupes.h>
#include <sys/socket.h>
 
#include <sys/un.h>
struct sockaddr_un {
	short sun_family; // AF_UNIX
	char sun_path[108];
};
 
#include <netinet/in.h>
struct sockaddr_in {
	short sin_family; // == AF_INET
	u_short sin_port; // port number
	struct in_addr sin_addr; // host IP address
	char sin_zero[8]; // not used
}
int bind(int sockfd, struct sockaddr *addr, int addrlen);
  • sockfd - дескриптор сокета.
  • addr - указатель на структуру, содержащую адрес сокета.
  • addrlen - размер структуры в байтах.

Возвращает 0 или -1, в случае ошибки

Часть портов зарезервирована. Частью можно пользоваться.

Есть специальные IP для связывания программ внутри одной машины: 127.0.0.1 и localhost.

Прослушивание сокета

#include <sys/types.h>
#include <sys/socket.h>
 
int listen(int sockfd, int backlog);
  • sockfd - дескриптор сокета.
  • backlog - максимальный размер очереди запросов на соединение.

Возвращает 0 или -1, в случае ошибки. Код ошибки в errno.

Концепции Mark

Предварительное установление соединения

img

Клиент-сервер на сокетах.

Сервер

  1. socket() - На сервере создаём сокет
  2. bind() - Сокету даётся имя через
  3. listen() - Сервер вызывает метод (man читаем)
  4. accept() - обработка запроса (блокировка, если запросов нет). Если подключение принято, accept() создаёт под клиента ещё один сокет.
  5. send() <-> reсv() - пользователь отправляет запросы, сервер отвечает.
  6. shutdown()
  7. close()

Таблица файловых дескрипторов ограничена (10 по умолчанию). Пользователей много. Таблица очень быстро закончится.
Поэтому после accept() следует делать fork(). Выделять под пользователя новый процесс.

Клиент

  1. socket() -
  2. (bind() - не обязательно)
  3. connect() -

Читаем в man про запрос, превосходящий размер очереди, определённой в listen

Сокеты без предварительного соединения

Датаграммные сокеты (для отправки одного сообщения)

1

  1. socket()
  2. bind()
  3. sedto() <-> recvfrom()
  4. (shutdown)
  5. …

2

  1. socket()
  2. …

Запрос на соединение Mark

#include <sys/types.h>
 
int 

Подтверждение соединения Mark

#include <
 
int accept(int sockfd, struct sockaddr *addr, int *addrlen)

Приём и передача данных Mark

#include <
 
 

Необходимые заголовочные файлы и прототип

#include <sys/types.h>
#include <sys/socket.h>

Завершение работы с сокетом

#include <sys/types.h>
#include <sys/types.h>
 
int shutdown(int sockfd, int mode) // жёсткое закрытие
 
int close(int fd) // мягкое закрытие
  • sockfd - дескриптор сокета.
  • mode - режим закрытия соединения.
    • 0 - сокет закрывается для чтения.
    • 1 - сокет закрывается для записи.
    • 2 - сокет закрывается и для чтения, и для записи.

Возвращают 0 в случае успеха. -1 и errno в случае ошибки.

shutdown() может закрыть сокет полностью или частично.
close() закрывает только текущий дескриптор.

Проблема NUXI - Big-endian и little-endian при вызове bind() Mark

Разные устройства в сети могут использовать различные записи двоичных чисел. Также машины могут иметь разную разрядность.

Для решения проблемы есть специальные системные вызовы.

Пример: сокеты с установлением соединения в localhost.

Клиент

#include <
#include <
#include <
#include <
#include <
#include <
#define PORTNUM 50500
#define BUFLEN 80
 
int main(int argc, char **argv) {
	struct 
}

Сервер

 

Запустить на своей машине. Поиграться.

Пример: сокеты без предварительного установления соединения на localhost.

AF_INET - Процесс 1

 

AF_INET - Процесс 2