Лаборатория Tarantool

Системное программирование

Лекция 8:

Сеть. Модели TCP/IP, OSI. Связь с ядром. Интерфейсы и примеры.

Новости

Дедлайны:

  • планировщик корутин: 16 октября, штраф -10
  • shell: 30 октября, штраф -10
  • файловая система: 15 ноября, штраф -1
  • пул потоков: 29 ноября

IPC

Анонимные

int
pipe2(int pipefd[2], int flags);

void *
mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

int
mkfifo(const char *pathname, mode_t mode);

Именованные, стандарт XSI

int
semget(key_t key, int nsems, int semflg);

int
msgget(key_t key, int msgflg);

int 
shmget(key_t key, size_t size, int shmflg);

Именованные, стандарт POSIX

sem_t *
sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

IPC. Доменные UNIX сокеты

int
socket(int domain, int type, int protocol);

int
bind(int sockfd, const struct sockaddr *addr,
     socklen_t addrlen);

int
listen(int sockfd, int backlog);

int
connect(int sockfd, const struct sockaddr *addr,
        socklen_t addrlen);

int
accept(int sockfd, struct sockaddr *addr,
       socklen_t *addrlen);

Создание сокета с заданными доменом, протоколом, типом

Привязка имени к сокету

Прослушка входящих подключений через connect()

Способ подключить сокет к другому уже именованному сокету. Тогда bind не нужен

Если же сделали bind + listen, то сокет принимает клиентов через accept

IPC. Доменные UNIX сокеты

Способы использования

int fd = socket();
connect(fd, remote_addr);
/* Ready to read/write fd. */

Подключение к уже именованному

Создание именованного без соединения

int fd = socket();
bind(fd, addr);
/** Ready to read/write fd. */

Создание именованного с соединением

int fd = socket();
bind(fd, addr);
listen(fd);
while(1) {
        int remote_fd = accept(fd);
        /*
         * Ready to read/write
         * remote_fd.
         */
}

Connect() создает на сервере парный сокет, и эти пара может общаться как socketpair()

int fd2 = socket();
bind(fd2, addr2);
/** Ready to read/write fd2. */

read/write

send/recv

sendto/recvfrom

Без connect() работают только пакетные типы сокетов, и нужно указывать адресатов руками

DARPA и ARPANET

1963

Возникновение идеи

1965

Первое сетевое взаимодействие

1990

Популяризация сетей и закрытие ARPANET

Джозеф Карл Робнетт
Ликлайдер с работой "Галактическая сеть"

Начало ARPANET в DARPA - Defense Advanced Research Projects Agency

Коммутация каналов

0/100

0/200

0/70

0/50

50

50/100

50/200

50/50

50/70

0/50

50/50

  • Всегда нужна установка соединения
  • Данные идут по одному маршруту
  • Было в телефонах, пытались адаптировать для сетей
  • Занята канальная емкость, даже без данных
  • Неустойчивость к сбоям

Как работает

Результат

Коммутация пакетов

  • Разбивать данные на пакеты
  • Каждый пакет добирается сам

0/100

0/200

0/70

0/50

25

25/100

25/200

10/50

25/70

0/50

10/50

25

10

0/100

15/100

0/100

15/100

0/100

15/100

0/100

25/100

15

15

15

10

10

15

25

25

25

  • Масштабируемость
  • "Живучесть"

Как работает

Результат

Пакет

Метаданные физической среды

Метаданные маршрутизации

Метаданные корректора ошибок

Пользовательские данные

<html>...</html>

File size, blocks ...

Game data ...

User space

Kernel space

Приложения, веб-сервера, игры, файловые менеджеры, почта ...

Протоколы надежности доставки

Протоколы приема/продвижения пакетов дальше по сети

Протоколы взаимодействия с проводами, радио-средой

Полезная

нагрузка

Служебный

заголовок

Служебный

заголовок

Служебный

заголовок

Сборка пакета

User data

Приложение

Packet 1

Подсистема надежности

доставки

Header

Packet 2

Header

Подсистема маршрутизации

Packet 1

Header

Packet 2

Header

Header

Header

Packet 1

Header

Packet 2

Header

Header

Header

Header

Header

Драйвер сетевого устройства

Сетевые модели. OSI

OSI - Open System Interconnection

7 уровней:

  1. Приложений
  2. Представления
  3. Сессий
  4. Транспортный
  5. Сетевой
  6. Канальный
  7. Физический

OSI. Уровень приложений

Веб-страницы

Фильмы

Документы

Сетевые интерфейсы баз данных

Задачи:

  • создание данных
  • взаимодействие с пользователем

Особенности:

осведомленность про смысл данных, их назначение

Протоколы:

FTP, SMTP, DNS, HTTP

OSI. Уровень приложений

Пример протокола уровня приложений - формат ответа на SQL запрос в Tarantool

+----------------------------------------------+
| IPROTO_BODY: {                               |
|     IPROTO_METADATA: [                       |
|         {                                    |
|             IPROTO_FIELD_NAME: string,       |
|             IPROTO_FIELD_TYPE: number,       |
|             IPROTO_FIELD_FLAGS: number,      |
|         },                                   |
|         ...                                  |
|     ],                                       |
|                                              |
|     IPROTO_SQL_INFO: {                       |
|         SQL_INFO_ROW_COUNT: number,          |
|         SQL_INFO_LAST_ID: number,            |
|         ...                                  |
|     },                                       |
|                                              |
|     IPROTO_DATA: [                           |
|         tuple/scalar,                        |
|         ...                                  |
|     ]                                        |
| }                                            |
+----------------------------------------------+

OSI. Уровень приложений

Пример HTTP запроса

:authority: clc.stackoverflow.com
:method: GET
:path: /markup.js?...
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
cache-control: no-cache
cookie: prov=702db90b-56ab-53f7-3894-c3733607b954;...
pragma: no-cache
referer: https://stackoverflow.com/questions/...
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X ...

OSI. Уровень представления

<xml>

</xml>
<html>

</html>
--
- 'YAML'
...
{
  "json":
}
0xa7 MsgPack

Задачи:

  • запаковка данных уровня приложений, форматирование
  • шифрование

Особенности:

  • нет информации про смысл данных
  • есть информация о структуре данных

Языки, форматы:

JSON, XML, YAML, HTML, MessagePack

OSI. Уровень представления

 JSON представление ответа GitHub API

{
  "action": "opened",
  "issue": {
    "url": "https://api.github.com/repos/octocat/Hello-World/issues/1347",
    "number": 1347,
    ...
  },
  "repository" : {
    "id": 1296269,
    "full_name": "octocat/Hello-World",
    "owner": {
      "login": "octocat",
      "id": 1,
      ...
    },
    ...
  },
  "sender": {
    "login": "octocat",
    "id": 1,
    ...
  }
}

OSI. Уровень представления

 XML представление ответа с opengis

<WFS_Capabilities xmlns="http://www.opengis.net/wfs" version="1.0.0">
  <Service>
    <Name> Oracle WFS </Name>
    <Title> Oracle Web Feature Service </Title>
    <Abstract> Web Feature Service maintained by Oracle </Abstract>
    <OnlineResource>http://localhost:8888/SpatialWS-</OnlineResource>
  </Service>
  <Capability>
  <GetCapabilities>
    <DCPType>
      <HTTP>
        <Get onlineResource="http://localhost:8888/SpatialWS-"/>
      </HTTP>
    </DCPType>
    <DCPType>
      <HTTP>
        <Post onlineResource="http://localhost:8888/SpatialWS-"/>
      </HTTP>
    </DCPType>
  </GetCapabilities>
</FeatureType>
</WFS_Capabilities>

OSI. Уровень сессий

Выбор уровня надежности

Аутентификация

Нужна ли установка соединения

Тайминги

Задачи:

  • выбор способа связи
  • аутентификация
  • выбор степени надежности связи
  • выбор таймаутов

Особенности:

Почти ничего не реализует, кроме аутентификации. Остальное лишь выбирает.

Протоколы:

PAP, CHAP

?

OSI. Первые три уровня

Приложения

Представление

Сессии

Какой что делает? Какие особенности у каждого?

Приложения: создать данные, знают их смысл.

Представление: форматировать данные, знают только вид данных.

Сессии: выбрать настройки связи, аутентификацию. Видит данные, как просто байты.

OSI. Транспортный уровень

Надежность: порядок, дублирования, потери, перегрузки

Фрагментация и батчинг

Установка виртуального соединения

Вид данных: пакеты или сплошной поток

Задачи:

  • установка виртуального соединения (если надо)
  • виртуализация пакетов, как потока байт (если надо)
  • разные степени надежности доставки

Особенности:

  • вид данных не важен, только размер
  • способ доставки и расположение адресата не важны

Протоколы:

TCP, UDP, SCTP, RDP

OSI. Сетевой уровень

Маршрутизация

Продвижение чужих пакетов

Борьба с некоторыми перегрузками

Задачи:

  • маршрутизация
  • продвижение чужих пакетов
  • борьба с сетевыми перегрузками (TTL)
  • сегментирование сетей

Особенности:

  • нет понятия соединения, только пакетная маршрутизация. Соединения - транспортный уровень
  • нет никакой надежности. После отправки пакет забывается
  • передача датаграммами

Протоколы:

IP, DDP, ICMP, RIP, EGP

OSI. Сетевой уровень

Сети иерархичны

На сетевом уровне взаимодействуют разные подсети

OSI. Сетевой уровень

Вася Колотушкин

Другие Васи Колотушкины

Васе К.

Какому из Вась?

Улица Пушкина, дом Колотушкина, Васе К.

OSI. Канальный уровень

Маршрутизация в одной подсети

Продвижение чужих пакетов в одной подсети

Борьба с некоторыми перегрузками

Задачи:

  • маршрутизация внутри одной подсети
  • продвижение чужих пакетов
  • борьба с сетевыми перегрузками (TTL)

Особенности:

  • нет деления на подсети. Используется внутри одной, небольшой
  • нет IP адресов
  • передача фреймами, кадрами

Протоколы:

Ethernet, MPLS, PPP, TokenRing

OSI. Канальный уровень

Область работы канального уровня

Область работы сетевого уровня

OSI. Физический уровень

Модуляция сигнала

Взаимодействие со средой передачи

Борьба с коллизиями

Задачи:

  • передать последовательность бит по проводу или радио-среде
  • избегать коллизий с другими передатчиками
  • исправлять ошибки шумов, влияния внешней среды

Особенности:

  • передача побитово
  • очень много математики и физики

Технологии:

Bluetooth, Wi-Fi, OTN, Ethernet cable, USB

Проверка и исправление ошибок

OSI. Физический уровень

Область работы канального уровня

Область работы физического уровня

OSI. Итог

Приложение

Представление

Сессии

Транспорт

Канал

Сеть

Физика

Data

Сетевые модели. TCP/IP

4 уровня:

  1. Приложений
  2. Транспортный
  3. Сетевой
  4. Канальный

TCP/IP

OSI

7 уровней:

  1. Приложений
  2. Представления
  3. Сессий
  4. Транспортный
  5. Сетевой
  6. Канальный
  7. Физический

Сеть в ядре

struct tcphdr {
	__be16	source;
	__be16	dest;
	__be32	seq;
	__be32	ack_seq;
	__u16	doff:4,
		res1:4,
		cwr:1,
		ece:1,
		urg:1,
		ack:1,
		psh:1,
		rst:1,
		syn:1,
		fin:1;
	__be16	window;
	__sum16	check;
	__be16	urg_ptr;
};
struct iphdr {
	__u8	version:4,
  		ihl:4;
	__u8	tos;
	__be16	tot_len;
	__be16	id;
	__be16	frag_off;
	__u8	ttl;
	__u8	protocol;
	__sum16	check;
	__be32	saddr;
	__be32	daddr;
};
struct ethhdr {
	unsigned char h_dest[ETH_ALEN];
	unsigned char h_source[ETH_ALEN];
	__be16	      h_proto;
};

Упаковка, заворачивание в заголовки

Сеть в ядре

int
tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
send(socket, data, data_size);
int
ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
		      __be32 saddr, __be32 daddr,
                      struct ip_options_rcu *opt);
int
dnet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
	        u16 value);

User space

Kernel space

API

int
socket(int domain, int type, int protocol);

AF_UNIX - локальный сокет, видимый только на этой машине

AF_INET - сетевые сокеты на базе IPv4

AF_PACKET - "сырые" сокеты, можно самому разбирать протоколы

API

int
socket(int domain, int type, int protocol);

SOCK_DGRAM - UDP, ограничен размер пакета, нет гарантий надежности

SOCK_STREAM - TCP, данные как поток байт, а не пакеты, есть гарантии доставки, соединения

SOCK_SEQPACKET - SCTP, как TCP, но данные как пакеты ограниченного размера

SOCK_RDM - RDP, как SCTP, но порядок доставки не гарантирован

API

int
socket(int domain, int type, int protocol);

0 - по умолчанию

IPPROTO_SCTP

IPPROTO_IP

IPPROTO_TCP

IPPROTO_RAW

IPPROTO_UDP

API

void try_protocol(int type, int protocol, const char *protocol_name)
{
	int sock = socket(AF_INET, type, protocol);
	if (sock == -1) {
		printf("%s: error = %s\n", protocol_name, strerror(errno));
	} else {
		printf("%s: success\n", protocol_name);
		close(sock);
	}
}

void try_type(int type, const char *type_name)
{
	printf("\nTry %s type\n", type_name);
	try_protocol(type, IPPROTO_TCP, "TCP");
	try_protocol(type, IPPROTO_IP, "IP");
	try_protocol(type, IPPROTO_SCTP, "SCTP");
	try_protocol(type, IPPROTO_RAW, "RAW");
	try_protocol(type, IPPROTO_UDP, "UDP");
}

int main()
{
	try_type(SOCK_DGRAM, "DGRAM");
	try_type(SOCK_RAW, "RAW");
	try_type(SOCK_STREAM, "STREAM");
	return 0;
}

API

$> gcc 1_socket_protocol.c

$> ./a.out
Try DGRAM type
TCP: error = Protocol wrong type for socket
IP: success
SCTP: error = Protocol not supported
RAW: error = Protocol wrong type for socket
UDP: success

Try RAW type
TCP: success
IP: success
SCTP: success
RAW: success
UDP: success

Try STREAM type
TCP: success
IP: success
SCTP: error = Protocol not supported
RAW: error = Protocol wrong type for socket
UDP: error = Protocol wrong type for socket

Mac

$> gcc 1_socket_protocol.c

$> ./a.out
Try DGRAM type
TCP: error = Protocol not supported
IP: success
SCTP: error = Protocol not supported
RAW: error = Protocol not supported
UDP: success

Try RAW type
TCP: success
IP: error = Protocol not supported
SCTP: success
RAW: success
UDP: success

Try STREAM type
TCP: success
IP: success
SCTP: success
RAW: error = Protocol not supported
UDP: error = Protocol not supported

Linux

API

int
bind(int sockfd, const struct sockaddr *addr,
     socklen_t addrlen);
struct sockaddr {
        sa_family_t sa_family;
        char sa_data[14];
};
struct sockaddr_in {
        sa_family_t sin_family;
        in_port_t sin_port;
        struct in_addr sin_addr;
};
struct sockaddr_un {
        sa_family_t sun_family;
        char sun_path[108];
};
struct sockaddr_nl {
        sa_family_t nl_family;
        unsigned short nl_pad;
        pid_t nl_pid;
        __u32 nl_groups;
};

AF_UNIX

AF_INET

API

struct sockaddr_in {
        sa_family_t sin_family;
        in_port_t sin_port;
        struct in_addr sin_addr;
};

struct in_addr {
        uint32_t s_addr;
};

AF_INET

Адрес назначения транспортного уровня, порт, 2 байта

Адрес назначения сетевого уровня, IP адрес, 4 байта:

xxx.xxx.xxx.xxx

Порт и адрес в network byte order, big-endian

uint32_t
htonl(uint32_t hostlong);

uint16_t
htons(uint16_t hostshort);

uint32_t
ntohl(uint32_t netlong);

uint16_t
ntohs(uint16_t netshort);
int
inet_aton(const char *cp, struct in_addr *inp);

in_addr_t
inet_addr(const char *cp);

in_addr_t
inet_network(const char *cp);

char *
inet_ntoa(struct in_addr in);

struct in_addr
inet_makeaddr(in_addr_t net, in_addr_t host);

API

int
getaddrinfo(const char *node, const char *service,
            const struct addrinfo *hints,
            struct addrinfo **res);

void
freeaddrinfo(struct addrinfo *res);

const char *
gai_strerror(int errcode);

Для преобразования доменного имени в адрес для bind/connect

API. Пример поиска адреса

int
main()
{
	struct addrinfo *addr, *iter;
	int rc = getaddrinfo("yandex.ru", NULL, NULL, &addr);
	if (rc != 0) {
		printf("Error = %s\n", gai_strerror(rc));
		return -1;
	}
	printf("Families: inet = %d, inet6 = %d\n", AF_INET, AF_INET6);
	printf("Socket types: dgram = %d, stream = %d, raw = %d\n", SOCK_DGRAM,
	       SOCK_STREAM, SOCK_RAW);
	printf("Protocols: tcp = %d, udp = %d\n\n", IPPROTO_TCP, IPPROTO_UDP);
	for (iter = addr; iter = iter->ai_next; iter != NULL) {
		printf("family = %d, socktype = %d, protocol = %d",
		       iter->ai_family, iter->ai_socktype, iter->ai_protocol);
		if (iter->ai_family == AF_INET) {
			char buf[128];
			struct sockaddr_in *tmp =
				(struct sockaddr_in *)iter->ai_addr;
			inet_ntop(AF_INET, &tmp->sin_addr, buf, sizeof(buf));
			printf(", ip = %s", buf);
		}
		printf("\n");
	}
	freeaddrinfo(addr);
	return 0;
}

Получить начало списка адресов

Распечатать каждый из адресов

Элементы связаны через ai_next поле

В каждом элементе описание адреса и сокета, который на нем слушает

struct addrinfo {
        int ai_flags;
        int ai_family;
        int ai_socktype;
        int ai_protocol;
        socklen_t ai_addrlen;
        struct sockaddr *ai_addr;
        char *ai_canonname;
        struct addrinfo *ai_next;
};

API. Пример поиска адреса

$> gcc 2_getaddrinfo.c

$> ./a.out
Families: inet = 2, inet6 = 10
Socket types: dgram = 2, stream = 1, raw = 3
Protocols: tcp = 6, udp = 17

family = 2, socktype = 2, protocol = 17, ip = 77.88.55.80
family = 2, socktype = 3, protocol = 0, ip = 77.88.55.80
family = 2, socktype = 1, protocol = 6, ip = 5.255.255.88
family = 2, socktype = 2, protocol = 17, ip = 5.255.255.88
family = 2, socktype = 3, protocol = 0, ip = 5.255.255.88
family = 2, socktype = 1, protocol = 6, ip = 77.88.55.77
family = 2, socktype = 2, protocol = 17, ip = 77.88.55.77
family = 2, socktype = 3, protocol = 0, ip = 77.88.55.77
family = 2, socktype = 1, protocol = 6, ip = 5.255.255.80
family = 2, socktype = 2, protocol = 17, ip = 5.255.255.80
family = 2, socktype = 3, protocol = 0, ip = 5.255.255.80
family = 10, socktype = 1, protocol = 6
family = 10, socktype = 2, protocol = 17
family = 10, socktype = 3, protocol = 0

API. Пример клиента

int
main(int argc, const char **argv)
{
	int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == -1) {
		printf("error = %s\n", strerror(errno));
		return -1;
	}
	struct addrinfo *addr;
	struct addrinfo filter;
	memset(&filter, 0, sizeof(filter));
	filter.ai_family = AF_INET;
	filter.ai_socktype = SOCK_STREAM;
	int rc = getaddrinfo(argv[1], argv[2], &filter, &addr);
	if (rc != 0) {
		printf("addrinfo error = %s\n", gai_strerror(rc));
		close(sock);
		return -1;
	}
	if (addr == NULL) {
		printf("not found a server\n");
		freeaddrinfo(addr);
		close(sock);
		return -1;
	}

Создал сокет для работы по TCP

Пытаюсь получить адрес сервера по имени

Вместо итерации по списку можно задать фильтр для результатов - тогда хватит одного адреса

	rc = connect(sock, addr->ai_addr, addr->ai_addrlen);
	freeaddrinfo(addr);
	if (rc != 0) {
		printf("connect error = %s\n", strerror(errno));
		close(sock);
		return -1;
	}
	int number;
	while (scanf("%d", &number) > 0) {
		if (write(sock, &number, sizeof(number)) == -1) {
			printf("error = %s\n", strerror(errno));
			continue;
		}
		printf("Sent %d\n", number);
		number = 0;
		int rc = read(sock, &number, sizeof(number));
		if (rc == 0) {
			printf("Closed connection\n");
			break;
		}
		if (rc == -1)
			printf("error = %s\n", strerror(errno));
		else
			printf("Received %d\n", number);
	}
	close(sock);
	return 0;
}

Далее ничего не отличается от UNIX сокетов

API. Пример сервера

void *
worker_f(void *arg)
{
	printf("New client created\n");
	int client_sock = (int) arg;
	while(1) {
		int buffer = 0;
		ssize_t size = read(client_sock, &buffer, sizeof(buffer));
		if (size == -1) {
			printf("error = %s\n", strerror(errno));
			continue;
		}
		if (size == 0) {
			printf("Closed connection\n");
			break;
		}
		printf("Received %d\n", buffer);
		buffer++;
		if (write(client_sock, &buffer, sizeof(buffer)) == -1)
			printf("error = %s\n", strerror(errno));
		else
			printf("Sent %d\n", buffer);
	}
	close(client_sock);
	return NULL;
}

В обслуживании клиентов ничего не меняется по сравнению с UNIX сокетами

Прочитать число, увеличить

Послать обратно

int
main(int argc, const char **argv)
{
	int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == -1) {
		printf("error = %s\n", strerror(errno));
		return -1;
	}
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(12345);
	inet_aton("127.0.0.1", &addr.sin_addr);

	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
		printf("bind error = %s\n", strerror(errno));
		return -1;
	}
	if (listen(sock, 128) == -1) {
		printf("listen error = %s\n", strerror(errno));
		return -1;
	}

Создал TCP сокет

Вместо getaddrinfo могу сам заполнить адрес

Дальше ничего не меняется от UNIX сокетов

	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, 1);
	while(1) {
		pthread_t worker_thread;
		int client_sock = accept(sock, NULL, NULL);
		if (client_sock == -1) {
			printf("error = %s\n", strerror(errno));
			continue;
		}
		int rc = pthread_create(&worker_thread, &attr, worker_f,
					(void *) client_sock);
		if (rc != 0) {
			printf("error = %s\n", strerror(rc));
			close(client_sock);
		}
	}
	pthread_attr_destroy(&attr);
	close(sock);
	return 0;
}

Дальше все остается как было. Новый клиент берется через accept и кладется в свой поток

API. Опции сокетов

int
getsockopt(int sockfd, int level, int optname,
           void *optval, socklen_t *optlen);

int
setsockopt(int sockfd, int level, int optname,
           const void *optval, socklen_t optlen);

int
fcntl(int fd, int cmd, ... /* arg */ );

SO_KEEPALIVE

SO_REUSEADDR

Игнорировать допосылку данных ядром из уже закрытого сокета

Периодическая посылка пустых пакетов во время бездействия, чтобы вовремя обнаружить разрыв соединения

API. Опции сокетов

SO_REUSEPORT

{protocol, src_ip, src_port, dst_ip, dst_port}

Опция разрешает этим полям совпадать, даже у живых сокетов

Идентификатор каждого сокета в ядре:

O_NONBLOCK

Не блокироваться на чтение/запись, если читать/писать нечего

API

int enable = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int));
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(int));

flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

Заключение

Обратная связь: goo.gl/forms/TAeraYrqJcil7GDt1

и на портале Техносферы

В следующий раз:

Advanced IO. Неблокирующие IO операции. Блокировка файла. Мультиплексирование: select, poll, kqueue.

Системное программирование 8

By Vladislav Shpilevoy

Системное программирование 8

Сеть, краткая история от ARPANET. Каноническая модель OSI, реальная TCP/IP, стек протоколов. Реализация сетевого взаимодействия в ядре. Пользовательский интерфейс socket, connect, close, send, recv. TCP и UDP.

  • 51
Loading comments...

More from Vladislav Shpilevoy