Чтение онлайн

ЖАНРЫ

Разработка приложений в среде Linux. Второе издание

Троан Эрик В.

Шрифт:

17.5.8. Клиентские приложения TCP

Клиенты TCP подобны клиентам домена Unix. Как правило, сразу же после создания сокета, клиент подключается к серверу с помощью функции

connect
. Единственное различие состоит в способе передачи адреса в
connect
. Вместо того, чтобы использовать имя файла, большинство клиентов TCP отыскивают имя хоста через функцию
getaddrinfo
, которая предоставляет информацию для
connect
.

Ниже приводится несложный TCP-клиент, который взаимодействует с сервером, представленным в предыдущем разделе. Он принимает один аргумент: имя хоста, на котором работает сервер, или его IP-номер (в десятичном представлении с разделительными точками). Во всем остальном программа ведет себя также как клиент сокета домена Unix, показанный ранее в этой главе.

 1: /* tclient.с */

 2:

 3: /*
Подключиться к серверу, чье имя хоста или IP-адрес переданы в качестве

 4: аргумента, на порте 4321. После соединения скопировать все содержимое

 5: stdin в сокет, затем завершить работу. */

 6:

 7: #include <arpa/inet.h>

 8: #include <netdb.h>

 9: #include <netinet/in.h>

10: #include <stdio.h>

11: #include <stdlib.h>

12: #include <string.h>

13: #include <sys/socket.h>

14: #include <unistd.h>

15:

16: #include "sockutil.h" /* некоторые служебные функции */

17:

18: int main(int argc, const char ** argv) {

19: struct addrinfo hints, *addr;

20: struct sockaddr_in * addrinfo;

21: int rc;

22: int sock;

23:

24: if (argc !=2) {

25: fprintf(stderr, "поддерживается только одиночный аргумент\n");

26: return 1;

27: }

28:

29: memset(&hints, 0, sizeof(hints));

30:

31: hints.ai_socktype = SOCK_STREAM;

32: hints.ai_flags = AI_ADDRCONFIG;

33: if ((rc = getaddrinfo(argv[1], NULL, &hints, &addr))) {

34: fprintf(stderr, "сбой поиска имени хоста: %s\n",

35: gai_strerror(rc));

36: return 1;

37: }

38:

39: /* это позволяет получить доступ к sin_family и sin_port

40: (которые расположены там же, где и sin6_family и sin6_port) */

41: addrinfo = (struct sockaddr_in *) addr->ai_addr;

42:

43: if ((sock = socket(addrInfo->sin_family, addr->ai_socktype,

44: addr->ai_protocol)) < 0)

45: die("socket");

46:

47: addrInfo->sin_port = htons(4321);

48:

49: if (connect(sock, (struct sockaddr *) addrinfo,

50: addr->ai_addrlen))

51: die("connect");

52:

53: freeaddrinfo(addr);

54:

55: copyData(0, sock);

56:

57: close(sock);

58:

59: return 0;

60: }

17.6. Использование дейтаграмм UDP

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

• Протоколы без соединений обрабатывают перезапуски машин более плавно, поскольку нет необходимости в переустановке соединений. Это очень заманчивое свойство для сетевых файловых систем (таких как NFS,

действующей на основе UDP), поскольку оно позволяет перезапускать файловый сервер без уведомления клиента.

• Простейшие протоколы могут работать гораздо быстрее через дейтаграммный протокол. Служба имен доменов DNS использует UDP только по этой причине (несмотря на то что наряду с этим дополнительно поддерживается TCP). При установке соединения TCP клиентская машина отправляет сообщение на сервер, получает от сервера подтверждение, указывающее на активность соединения, затем сообщает серверу о том, что установлена клиентская сторона соединения [137] 23. После этого клиент может отправить свой запрос имени хоста на взаимодействующий сервер. Все это в итоге составляет процесс из пяти сообщений, не считая проверки ошибок и ожидания фактического отправления запроса и ответа на него. Используя UDP, запросы имени хоста пересылаются как первый пакет на сервер, который отвечает одним или более UDP-пакетами, тем самым уменьшая общий счетчик пакетов до пяти. Если клиент не получает ответ, то он просто перепосылает запрос.

137

Это называется трехсторонним квитированием TCP, которое на самом деле проходит несколько сложнее, чем описано выше.

• При первичной установке компьютеров часто требуется установить для них IP-адрес, а затем загрузить первую часть операционной системы через сеть [138] . Применение UDP для подобных операций создает набор протоколов, который внедряется в такие машины гораздо проще, чем, если бы требовалась полная TCP-реализация.

17.6.1. Создание UDP-сокета

Как и любой другой сокет, UDP-сокет создается с помощью функции

socket
, однако второй аргумент должен быть
SOCK_DGRAM
, а последний — либо
IPPROTO_UDP
, либо просто ноль (так как UDP является стандартным IP-дейтаграммным протоколом).

138

Этот процесс называется сетевой загрузкой.

После создания сокета ему необходимо присвоить номер локального порта. Это происходит тогда, когда программа удовлетворяет одному из следующих трех условий.

• Номер порта задается явно через вызов функции bind. Этот шаг является обязательным для тех серверов, для которых необходимо получение дейтаграмм на номер официального порта. Системный вызов в точности совпадает с системным вызовом для TCP-серверов.

• Дейтаграмма посылается через сокет. Ядро присваивает данному сокету номер порта UDP при первой передаче данных через него. В большинстве клиентских программ применяется именно этот прием, поскольку номер используемого порта для них не имеет значения.

• Для сокета устанавливается удаленный адрес через функцию

connect
(которая является дополнительной для UDP-сокетов).

Также существует два различных способа присвоения номера удаленного порта. Вспомните о том, что TCP-сокеты имеют удаленный адрес, который присваивается через

connect
. Этот адрес может использоваться и для UDP-сокетов [139] . При этом функция
connect
для TCP вызывает обмен пакетами для инициализации соединения (что делает
connect
медленным системным вызовом), в то время как вызов
connect
для UDP-сокетов просто присваивает удаленный IP-адрес и номер порта для исходящих дейтаграмм (и является быстрым системным вызовом). Еще одно различие состоит в том, что приложения могут подключаться к TCP-сокету только один раз; UDP-сокеты могут повторно использовать свои адреса назначения [140] .

139

UDP-сокеты, которые имеют постоянные пункты назначения, присвоенные через функцию

connect
, иногда называются присоединенными UDP-сокетами.

140

Есть также возможность превратить подключенный сокет в неподключенный с помощью функции

connect
, однако эта процедура не стандартизирована. Если вам все же необходимо ее применить, обратитесь к [33].

Поделиться с друзьями: