Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_shell_код_Фостер_Дж_

.pdf
Скачиваний:
14
Добавлен:
19.04.2024
Размер:
3.68 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

Компиляция

 

 

 

 

 

n

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Клиенты и серверы для протокола TCP 151

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

(foster@syngress ~/book) $ gcc -o client1 client1.c

(foster@syngress ~/book) $ ./client1 usage: ./client1: ip_address port

Пример выполнения

(foster@syngress ~/book) $ ./client1 127.0.0.1 80 TCP-клиент установил соединение.

TCP-клиент закрыл соединение.

(foster@syngress ~/book) $ ./client1 127.0.0.1 81 TCP-клиент: ошибка connect().

Программа client1.c ожидает два аргумента в командной строке: IP-адрес и номер порта, с которым должен соединиться клиент. Она получает от системы дескриптор сокета и устанавливает соединение с указанным адресом и портом. Данные не передаются. Сокет сразу закрывается. Если соединение установить не удается, печатается сообщение об ошибке и программа завершается.

Анализ

В строке 30 программа получает дескриптор сокета, вызвав функцию socket(). В качестве аргумента domain ей передается константа AF_INET, которая говорит, что этот сокет будет работать по протоколу IP. Аргумент type равен SOCK_STREAM, то есть протоколом транспортного уровня будет TCP. В качестве идентификатора протокола передается 0, поскольку этот аргумент обычно не используется для сокетов, работающих по протоколу TCP.

В строке 37 инициализируется структура sockaddr_in, которая служит для идентификации удаленной оконечной точки данного сокета.

В строке 39 задается семейство протоколов (domain) для удаленной оконечной точки AF_INET, и это значение соответствует аргументу функции socket(), заданному в строке 28.

В строке 40 задается номер удаленного порта. Этот порт был указан

âкомандной строке и передан программе в виде указателя на массив символов (char *). Массив преобразуется в 4-байтовое целое число (типа int) функцией atoi(). Затем это число преобразуется в двухбайтовое целое, записанное в сетевом порядке байтов. Результат помещается

âïîëå sin_port структуры sockaddr_in.

В строке 41 встречается IP-адрес удаленного компьютера, заданный

âкомандной строке и переданный в программу в виде указателя на мас-

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

i

 

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

o

 

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

152

Глава 3. BSD сокеты

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

o

 

w Click

 

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

e

 

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

сив символов (char *). Массив преобразуется в беззнаковое 32-битовое-x cha

 

 

 

 

 

значение с помошью функции inet_addr(). Оно записывается в поле sin_addr.s_addr структуры sockaddr_in.

В строках 43 и 44 сокет соединяется с удаленным хостом и портом. В этот момент происходит процедура трехшагового квитирования.

В строке 53 соединенный сокет закрывается и происходит разрыв соединения.

В примере 3.2 показано, как создается серверный TCP-сокет. В результате образуется оконечная точка, с которой могут соединиться клиенты типа client1.c.

Пример 3.2. TCP*сервер (server.c)

1 /*

2 * server1.c

3*

4* Создание серверного TCP-сокета, прием

5* запроса на соединение от одного TCP-клиента

6

*

с помощью

функций socket(), bind(), listen() и

7

*

accept().

 

8*

9

* foster <jamescfoster@gmail.com>

10

*/

 

11

 

 

12

#include <stdio.h>

13

#include <sys/types.h>

14

#include <sys/socket.h>

15

#include <netinet/in.h>

16

 

 

17 int

 

18 main

(int argc, char *argv[])

19{

20struct sockaddr_in sin ;

21struct sockaddr_in csin;

22

socklen_t

len

= sizeof(struct sockaddr);

23

short

port

= 0;

24

int

csock

= 0;

25

int

sock

= 0;

26

int

ret

= 0;

27

 

 

 

28if(argc != 2)

29{

30printf("usage: %s: port\n", argv[0]);

31return(1);

32}

33

34 port = atoi(argv[1]);

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

 

 

 

e

 

 

 

 

d

 

 

xch35

 

 

 

 

 

 

 

f-

 

an

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Клиенты и серверы для протокола TCP 153

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

36sock = socket(AF_INET, SOCK_STREAM, 0);

37if(sock < 0)

38{

39printf("TCP-сервер: ошибка socket().\n");

40return(1);

41}

42

43

memset(&sin, 0x0, sizeof(struct sockaddr_in *));

44

 

 

45

sin.sin_family

= AF_INET;

46

sin.sin_port

= htons(port);

47

sin.sin_addr.s_addr = INADDR_ANY;

48

 

 

49

ret = bind(sock, (struct sockaddr *)&sin,

50

 

(struct sockaddr));

51if(ret < 0)

52{

53printf("TCP-сервер: ошибка bind().\n");

54close (sock);

55return(1);

56}

57

58ret = listen(sock, 5);

59if(ret < 0)

60{

61printf("TCP-сервер: ошибка listen().\n");

62close (sock);

63return(1);

64}

65

66 printf("TCP-сервер прослушивает порт.\n");

67

68 memset(&csin, 0x0, sizeof(struct sockaddr));

69

70csock = accept(sock, (struct sockaddr *)&csin, &len);

71if(csock < 0)

72{

73printf("TCP-сервер: ошибка accept().\n");

74}

75else

76{

77

printf("TCP-сервер: соединение с клиентом "

\

78"на порту %d.\n", port);

79close(csock);

80}

81

82 close(sock);

83

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

154

Глава 3. BSD сокеты

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

return(0);

 

 

 

df-xchan

84

 

 

 

 

 

 

 

 

85 }

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Компиляция

(foster@syngress ~/book) $ gcc -o server1 server1.c

(foster@syngress ~/book) $ ./server1 usage: ./server1: port

Пример выполнения

(foster@syngress ~/book) $ ./server1 4001 TCP-клиент установил соединение. TCP-клиент закрыл соединение.

(foster@syngress ~/book) $ ./client1 127.0.0.1 81 TCP-сервер прослушивает порт.

Программа server1.c ожидает всего один аргумент в командной строке: номер порта, который сервер будет прослушивать в ожидании запроса на соединение от клиента. Она получает от системы дескриптор сокета с помощью функции socket(), затем привязывает сокет к указанному порту (bind()), переводит сокет в режим прослушивания (listen()) и вызывает функцию accept(), которая и ожидает запроса. Как только запрос на соединение получен, сокет сразу закрывается, соединение разрывается, программа завершает работу.

Анализ

В строке 36 программа получает дескриптор сокета, вызвав функцию socket(). В качестве аргумента domain ей передается константа AF_INET, которая говорит, что этот сокет будет работать по протоколу IP. Аргумент type равен SOCK_STREAM, то есть протоколом транспортного уровня будет TCP. В качестве идентификатора протокола передается 0, поскольку этот аргумент обычно не используется для сокетов, работающих по протоколу TCP.

В строке 43 инициализируется структура sockaddr_in, которая служит для идентификации локальной оконечной точки данного сокета.

В строке 45 задается семейство протоколов (domain) для удаленной оконечной точки AF_INET, и это значение соответствует аргументу функции socket(), заданному в строке 36.

В строке 46 задается номер локального порта. Этот порт был указан

в командной строке и передан программе в виде указателя на массив символов (char *). Массив преобразуется в 4-байтовое целое число

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

Клиенты и серверы для протокола TCP 155

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

df

 

e

 

(òèïà int) функцией atoi(). Затем это число преобразуется в двухбайто-

df

 

e

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

n

 

 

 

 

вое целое, записанное в сетевом порядке байтов. Результат помещается

âïîëå sin_port структуры sockaddr_in.

В строке 47 задается локальный IP-адрес, к которому будет привязан сокет. В качестве адреса указана целочисленная константа INADDR_ANY. Это означает, что сокет может принимать соединения с любого сетевого интерфейса, в том числе и возвратного (loopback). Если хост имеет несколько сетевых интерфейсов, то можно привязать сокет к конкретному интерфейсу, указав назначенный ему IP-адрес вместо

INADDR_ANY.

В строке 49 вызывается функция bind(), которая связывает информацию о локальной конечной точке, включая ее IP-адрес и порт, с дескриптором сокета.

В строке 58 вызывается функция listen(), которой в качестве второго аргумента передается максимальное число ожидающих соединений

âочереди. Если одновременно поступит большее число запросов, то

âсоединении будет отказано. Кроме того, функция переводит сокет

âсостояние готовности к приему соединений. Начиная с этого момента могут обрабатываться запросы на установление соединения, поступающие от клиентов.

В строке 70 вызывается функция accept(), принимающая запросы на соединение. Она блокирует (переводит в состояние ожидания) процесс до поступления нового запроса от клиента. Как только это произойдет, функция accept() вернет дескриптор сокета, соответствующего новому соединению.

В строке 82 сокет закрывается, так что новые соединения не могут быть установлены.

В примере 3.3 сначала запускается программа server1, а вслед за ней client1. Мы видим, что server1 получила дескриптор сокета, привязала его к порту, указанному в командной строке, и начала прослушивать порт в ожидании входящих соединений. После запуска client1 устанавливается TCP-соедине- ние. Затем обе программы закрывают свои концы соединения и завершают работу.

Пример 3.3. Клиент и сервер в действии

1 (foster@syngress ~/book) $ ./server1 4001 &

2 ./server1 4001 & (11) 31802

3

4 (foster@syngress ~/book) $ ./client1 127.0.0.1 4001

5 ./client1 127.0.0.1 4001

6

7 TCP-сервер: соединение с клиентом на порту 4001.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

156 Глава 3. BSD сокеты

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

8

 

 

 

df-xchan

e

 

 

 

 

 

9 TCP-клиент установил соединение.

10

11 [1] Done ./server1 4001

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Анализ

При запуске программы server1 указано, что она должна прослушивать TCP-порт 4001. В большинстве операционных систем порты с номерами от 1 до 1024 зарезервированы для привилегированных программ, поэтому в примере взят порт с номером больше 1024. Символ & в конце командной строки говорит, что программа server1 должна исполняться в фоновом режиме, так что сразу после ее запуска можно вводить следующую команду – запускающую client1.

В строке 1 оболочка tcsh печатает введенную команду.

В строке 2 tcsh печатает идентификатор фонового процесса, в котором исполняется программа server1.

В строке 4 запускается программа client1, которой передаются IP-адрес

127.0.0.1и порт 4001. Адрес 127.0.0.1 принадлежит возвратному интерфейсу. Это логический интерфейс, предназначенный для исполнения сетевых программ на локальной машине. В большинстве систем адресу

127.0.0.1соответствует доменное имя localhost.

В строке 5 tcsh печатает введенную команду.

В строке 7 server1 выводит сообщение о поступлении запроса на соединение от клиента, точнее, от программы client1.

В строке 9 client1 печатает сообщение о том, что онаустановила соединение с server1.

Теперь, когда у вас сложилось некоторое представление о программировании клиентских и серверных TCP-сокетов, перейдем к программированию UDP-сокетов.

Клиенты и серверы для протокола UDP

При программировании UDP-сокетов используются, в основном, те же приемы, что и для TCP-сокетов. Но UDP – это протокол без установления соединений, поэтому для него нужно меньше предварительных шагов и он предоставляет чуть больше гибкости при отправке и приеме UDP-датаграмм. UDP не является потоковым протоколом, данные передаются не побайтно, а целыми блоками – датаграммами.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

Клиенты и серверы для протокола UDP 157

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

В заголовке протокола UDP всего четыре поля: порт получателя, ïîðò îò-

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

Как и в случае TCP, UDP-сокеты создаются функцией socket(). Но UDP отличает способность отправлять и принимать датаграммы от различных хостов по одному-единственному сокету.

Типичная последовательность создания клиентского UDP-сокета начинается с вызова функции socket(). Затем надлежит определить адрес и номер порта удаленного хоста, которому сокет будет отправлять и от которого будет принимать данные. Дескриптор сокета передается функции connect(), которая запоминает, какой сокет в дальнейшем будет использоваться для передачи и приема. Вместо этого адрес и номер порта получателя можно указывать при каждой операции «записи», тогда один сокет можно будет использовать для обмена данными с разными хостами.

По протоколу UDP данные можно посылать с помощью функций write(), send() è sendto(). Чтобы можно было пользоваться функциями write() èëè send(), сокет нужно предварительно «соединить», вызвав функцию connect(). Если это не сделано, то остается функция sendto(), которой нужно передать IP-адрес и порт получателя. Читать данные по протоколу UDP позволяют функции read(), recv() è recvfrom(). Для использования функций read() è recv() сокет надо предварительно «соединить». Функция же recvfrom() получает IPадрес и номер порта из принятой датаграммы.

Данные, записанные в UDP-сокет, будут приняты в виде единого блока, а не в виде потока байтов, как в случае TCP. Каждый вызов write(), send() èëè sendto() порождает одну датаграмму. Принятые датаграммы считываются как неделимое целое. Если при попытке считывания датаграммы предоставлен буфер недостаточного размера, то функция чтения вернет код ошибки.

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

Пример 3.4. UDP*сокет (udp1.c)

1/*

2* udp1.c

3*

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

 

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

to

 

 

158

Глава 3. BSD сокеты

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

4

 

 

 

 

df-xchan

e

* пример программы для создания UDP-сокета

 

 

 

 

 

5*

6 * foster <jamescfoster@gmail.com>

7 */

8

9 #include <stdio.h>

10

11 #include <sys/socket.h>

12 #include <netinet/in.h>

13

14int

15main(void)

16{

17int sock = 0;

19sock = socket(AF_INET, SOCK_DGRAM, 0);

20if(sock < 0)

21{

22printf("ошибка socket().\n");

23}

24else

25{

26close(sock);

27printf("socket() завершилась успешно.\n");

28}

29

30return(0);

31}

Компиляция

obsd32# gcc -o udp1 udp1.c

Пример исполнения

obsd32# ./udp1

socket() завершилась успешно.

Анализ

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

В строках 11 и 12 включаются заголовочные файлы <sys/socket.h> è <netinet/in.h>. В них содержатся прототипы функций и структуры данных, необходимые для работы с сокетами.

В строке 19 вызывается функция socket(). Первый параметр – целочисленная константа AF_INET (определена в <sys/socket.h>). Она указывает, что сокет принадлежит адресному семейству AF_INET, которое соот-

ветствует адресации, принятой в протоколе IPv4.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Клиенты и серверы для протокола UDP 159

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-x cha

 

 

 

Второй параметр, переданный функции socket(), – это целочисленная

df

 

e

 

 

 

 

 

 

n

 

 

 

 

константа SOCK_DGRAM (определена в <sys/socket.h>). Она указывает тип создаваемого сокета. В сочетании с адресным семейством AF_INET òèï SOCK_DGRAM говорит о том, что нужно создать UDP-сокет.

Третий параметр socket() может содержать номер протокола, но при создании UDP-сокета он не используется и поэтому оставлен равным нулю.

Âслучае успеха функция socket() возвращает неотрицательное целое число. Оно однозначно идентифицирует сокет в текущем процессе и называется дескриптором сокета. В случае ошибки возвращается –1.

Âстроке 19 проверяется значение, которое вернула socket(). Если оно меньше нуля, то на стандартный вывод печатается сообщение об ошибке.

Âстроке 26 правильный дескриптор сокета передается функции close(), которая закрывает сокет, делая его непригодным для дальнейшего использования.

В примере 3.5 иллюстрируется отправка UDP-датаграммы через сокет, который предварительно был «соединен» с помощью функции connect().

Пример 3.5. Отправка UDP*датаграммы функцией send() (udp2.c)

1 /*

2 * udp2.c

3*

4* отправка UDP-датаграммы с помощью

5* сокета, который был предварительно

6 * обработан функцией connect().

7*

8 * foster <jamescfoster@gmail.com>

9 */

10

11 #include <stdio.h>

12

13 #include <sys/socket.h>

14 #include <netinet/in.h>

15 #include <arpa/inet.h>

16

17#define UDP2_DST_ADDR "127.0.0.1"

18#define UDP2_DST_PORT 1234

19

20int

21main(void)

22{

23struct sockaddr_in sin;

24char buf[100];

25int sock = 0;

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

160

Глава 3. BSD сокеты

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

int ret = 0;

 

 

 

df-xchan

26

 

 

 

 

 

 

 

 

27

 

28sock = socket(AF_INET, SOCK_DGRAM, 0);

29if(sock < 0)

30{

31printf("ошибка socket().\n");

32return(1);

33}

34

35

memset(&sin, 0x0, sizeof(sin));

36

 

 

37

sin.sin_family

= AF_INET;

38

sin.sin_port

= htons(UDP2_DST_PORT);

39

sin.sin_addr.s_addr = inet_addr(UDP2_DST_ADDR);

40

 

 

41ret = connect(sock, (struct sockaddr *) &sin, sizeof(sin));

42if(ret < 0)

43{

44printf("ошибка connect().\n");

45return(1);

46}

47

48 memset(buf, 'A', 100);

49

50ret = send(sock, buf, 100, 0);

51if(ret != 100)

52{

53printf("ошибка send().\n");

54return(1);

55}

56

57close (sock);

58printf("send() завершилась успешно.\n");

60return(0);

61}

Компиляция

obsd32# gcc -o udp2 udp2.c

Пример исполнения

obsd32# ./udp2

send() завершилась успешно.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Программа udp2.c базируется на коде для работы с сокетами из примера udp1.c. Дополнительно в ней показано, как надо объявлять и инициализиро-