freebsd % grep -e ^ftp -e ^domain /etc/services
ftp-data 20/tcp #File Transfer [Default Data]
ftp 21/tcp #File Transfer [Control]
domain 53/tcp #Domain Name Server
domain 53/udp #Domain Name Server
ftp-agent 574/tcp #FTP Software Agent System
ftp-agent 574/udp #FTP Software Agent System
ftps-data 989/tcp # ftp protocol, data, over TLS/SSL
ftps 990/tcp # ftp protocol, control, over TLS/SSL
Следующая функция, getservbyport
, ищет службу по заданному номеру порта и (не обязательно) протоколу.
#include
struct servent *getservbyport(int port , const char * protname );
Возвращает: непустой указатель в случае успешного выполнения, NULL в случае ошибки
Значение аргумента port
должно быть записано в сетевом порядке байтов. Типичные примеры вызова этой функции приведены ниже:
struct servent *sptr;
sptr = getservbyport(htons(53), "udp"); /* DNS с использованием UDP */
sptr = getservbyport(htons(21), "tcp"); /* FTP с использованием TCP */
sptr = getservbyport(htons(21), NULL); /* FTP с использованием TCP */
sptr = getservbyport(htons(21), "udp"); /* этот вызов приведет к ошибке */
Последний вызов оказывается неудачным, поскольку нет службы, использующей порт 21 с протоколом UDP.
Помните, что некоторые номера портов используются с TCP для одной службы, а с UDP — для совершенно другой, например:
freebsd % grep 514 /etc/services
shell 514/tcp cmd #like exec, but automatic
syslog 514/udp
Здесь показано, что порт 514 используется командой rsh
с TCP и демоном syslog
с UDP. Это характерно для портов 512-514.
Пример: использование функций gethostbyname и getservbyname
Теперь мы можем изменить код нашего TCP-клиента времени и даты, показанный в листинге 1.1, так, чтобы использовать функции gethostbyname
и getservbyname
и принимать два аргумента командной строки: имя узла и имя службы. Наша программа показана в листинге 11.2. Эта программа также демонстрирует желательное поведение при установлении соединения со всеми IP-адресами сервера на узле, имеющем несколько сетевых интерфейсов: попытки продолжаются до тех пор, пока соединение не будет успешно установлено или пока не будут перебраны все адреса.
Листинг 11.2. Наш клиент времени и даты, использующий функции gethostbyname и getservbyname
//names/daytimetcpcli1.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd, n;
6 char recvline[MAXLINE + 1];
7 struct sockaddr_in servaddr;
8 struct in_addr **pptr;
9 struct in_addr *inetaddrp[2];
10 struct in_addr inetaddr;
11 struct hostent *hp;
12 struct servent *sp;
13 if (argc != 3)
14 err_quit("usage: daytimetcpcli1 ");
15 if ((hp = gethostbyname(argv[1])) == NULL) {
16 if (inet_aton(argv[1], &inetaddr) == 0) {
17 err_quit("hostname error for %s: %s", argv[1],
18 hstrerror(h_errno));
19 } else {
20 inetaddrp[0] = &inetaddr;
21 inetaddrp[1] = NULL;
22 pptr = inetaddrp;
23 }
24 } else {
25 pptr = (struct in_addr**)hp->h_addr_list;
26 }
27 if ((sp = getservbyname(argv[2], "tcp")) == NULL)
28 err_quit("getservbyname error for %s", argv[2]);
29 for (; *pptr != NULL; pptr++) {
30 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
31 bzero(&servaddr, sizeof(servaddr));
32 servaddr.sin_family = AF_INET;
33 servaddr.sin_port = sp->s_port;
34 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
35 printf("trying %s\n", Sock_ntop((SA*)&servaddr, sizeof(servaddr)));
36 if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) == 0)
37 break; /* успешное завершение */
38 err_ret("connect error");
39 close(sockfd);
40 }
41 if (*pptr == NULL)
42 err_quit("unable to connect");
43 while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {
44 recvline[n] = 0; /* null terminate */
45 Fputs(recvline, stdout);
46 }
47 exit(0);
48 }
Вызов функций gethostbyname и getservbyname
13-28
Первый аргумент командной строки — это имя узла, передаваемое в качестве аргумента функции gethostbyname
, а второй — имя службы, передаваемое в качестве аргумента функции getservbyname
. Наш код подразумевает использование протокола TCP, что мы указываем во втором аргументе функции getservbyname
. Если функции gethostbyname
не удается найти нужное имя, мы вызываем функцию inet_aton
(см. раздел 3.6), чтобы проверить, не является ли аргумент командной строки IP-адресом в формате ASCII. В этом случае формируется список из одного элемента — этого IP-адреса.
Перебор всех адресов
29-35
Теперь мы пишем вызовы функций socket
и connect
в цикле, который выполняется для каждого адреса сервера, пока попытка вызова функции connect
не окажется успешной или пока не закончится список серверов. После вызова функции socket
мы заполняем структуру адреса сокета Интернета IP-адресом и номером порта сервера. Хотя в целях увеличения производительности мы могли бы вынести из цикла вызов функции bzero
и последующие два присваивания, наш код легче читать в таком виде, как он представлен сейчас. Установление соединения с сервером редко является основным источником проблем с производительностью сетевого клиента.
Читать дальше
Конец ознакомительного отрывка
Купить книгу