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

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

Лекция 8:

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

Новости

Дедлайны:

  • планировщик корутин: 19 марта, штраф -10
  • shell: 2 апреля, штраф -10
  • файловая система: 16 апреля

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 сокеты [1]

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 сокеты [2]

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

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. Уровень приложений [1]

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

Фильмы

Документы

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

Задачи:

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

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

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

Протоколы:

FTP, SMTP, DNS, HTTP

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

Пример протокола уровня приложений - формат ответа на 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. Уровень приложений [3]

Пример 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. Уровень представления [1]

<xml>

</xml>
<html>

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

Задачи:

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

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

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

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

JSON, XML, YAML, HTML, MessagePack

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

 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. Уровень представления [3]

 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. Первые три уровня

Приложения

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

Сессии

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

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

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

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

2 балла

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

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

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

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

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

Задачи:

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

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

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

Протоколы:

TCP, UDP, SCTP, RDP

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

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

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

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

Задачи:

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

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

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

Протоколы:

IP, DDP, ICMP, RIP, EGP

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

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

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

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

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

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

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

Задачи:

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

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

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

Протоколы:

Ethernet, MPLS, PPP, TokenRing

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

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

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

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

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

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

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

Задачи:

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

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

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

Технологии:

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

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

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

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

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

OSI. Итог

Приложение

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

Сессии

Транспорт

Канал

Сеть

Физика

Data

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

4 уровня:

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

TCP/IP

OSI

7 уровней:

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

Сеть в ядре [1]

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;
};

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

Сеть в ядре [2]

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 [1]

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

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

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

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

API [2]

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

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

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

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

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

API [3]

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

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

IPPROTO_SCTP

IPPROTO_IP

IPPROTO_TCP

IPPROTO_RAW

IPPROTO_UDP

API [4]

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 [5]

$> 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 [6]

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 [7]

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 [8]

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 [8]. Пример поиска адреса

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 != NULL; iter = iter->ai_next) {
		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 [9]. Пример поиска адреса

$> 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 [10]. Пример клиента

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 [11]. Пример сервера

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 [12]. Взаимодействие

$> gcc 3_server.c -o server
$> ./server
$> gcc 3_client.c.c -o client
$> ./client 127.0.0.1 12345
New client created
1
Sent 1
Received 2
Received 1
Sent 2
^C
$>
Closed connection

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

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 [14]. Опции сокетов

SO_REUSEPORT

{protocol, src_ip, src_port, dst_ip, dst_port}

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

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

O_NONBLOCK

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

API [15]

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);

Практикум [1]

Пул потоков

Пул потоков - это множество потоков, которым можно поручать разные задачи для фонового выполнения, и потом асинхронно получать результат. Потоки не завершаются после задачи, а берут следующую. Если задач нет, то поток спит.

Есть интерфейс для создания пула потоков, складывания туда задач, забора результатов. Надо написать его реализацию.

Цена: 15 - 25 баллов.

Срок: 2 недели.

Положить на ваш гитхаб и сказать мне его. Сдавать как угодно - лично/удаленно.

Практикум [2]

/** Thread pool API. */

int
thread_pool_new(int max_thread_count, struct thread_pool **pool);

int
thread_pool_thread_count(const struct thread_pool *pool);

int
thread_pool_delete(struct thread_pool *pool);

int
thread_pool_push_task(struct thread_pool *pool, struct thread_task *task);

/** Thread pool task API. */

int
thread_task_new(struct thread_task **task, thread_task_f function, void *arg);

bool
thread_task_is_finished(const struct thread_task *task);

bool
thread_task_is_running(const struct thread_task *task);


int
thread_task_join(struct thread_task *task, void **result);

int
thread_task_delete(struct thread_task *task);

Практикум [3]

struct thread_task {
	thread_task_f function;
	void *arg;

	/* PUT HERE OTHER MEMBERS */
};

struct thread_pool {
	pthread_t *threads;

	/* PUT HERE OTHER MEMBERS */
};

Заключение

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

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

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

By Vladislav Shpilevoy

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

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

  • 106
Loading comments...

More from Vladislav Shpilevoy