наших клиента и сервера не изменяются. Мы определяем одну структуру для двух аргументов, другую структуру для результата и помещаем оба определения в наш заголовочный файл
sum.h
, представленный в листинге 5.12. В листинге 5.13 показана функция
str_cli
.
Листинг 5.12. Заголовочный файл sum.h
//tcpcliserv/sum.h
1 struct args {
2 long arg1;
3 long arg2;
4 };
5 struct result {
6 long sum;
7 };
Листинг 5.13.
Функция str_cli, отправляющая два двоичных целых числа серверу
//tcpcliserv/str_cli09.c
1 #include "unp.h"
2 #include "sum.h"
3 void
4 str_cli(FILE *fp, int sockfd)
5 {
6 char sendline[MAXLINE];
7 struct args args;
8 struct result result;
9 while (Fgets(sendline, MAXLINE, fp) != NULL) {
10 if (sscanf(sendline, "%ld%ld", &args.arg1, &args.arg2) != 2) {
11 printf("invalid input, %s", sendline);
12 continue;
13 }
14 Writen(sockfd, &args, sizeof(args));
15 if (Readn(sockfd, &result, sizeof(result)) == 0)
16 err_quit("str_cli: server terminated prematurely");
17 printf("%ld\n", result.sum);
18 }
19 }
10-14
Функция
sscanf
преобразует два аргумента из текстовых строк в двоичные. Мы вызываем функцию
writen
для отправки структуры серверу.
15-17
Мы вызываем функцию
readn
для чтения ответа и выводим результат с помощью функции
printf
.
В листинге 5.14 показана наша функция
str_echo
.
Листинг 5.14. Функция str_echo, складывающая два двоичных целых числа
//tcpcliserv/str_echo09.c
1 #include "unp.h"
2 #include "sum.h"
3 void
4 str_echo(int sockfd)
5 {
6 ssize_t n;
7 struct args args;
8 struct result result;
9 for (;;) {
10 if ((n = Readn(sockfd, &args, sizeof(args))) == 0)
11 return; /*
соединение закрыто удаленным концом */
12 result.sum = args.arg1 + args.arg2;
13 Writen(sockfd, &result, sizeof(result));
14 }
15 }
9-14
Мы считываем аргументы при помощи вызова функции
readn
, вычисляем и запоминаем сумму и вызываем функцию
writen
для отправки результирующей структуры обратно.
Если мы запустим клиент и сервер на двух машинах с аналогичной архитектурой, например на двух компьютерах SPARC, все будет работать нормально:
solaris % tcpcli09 12.106.32.254
11 22мы вводим эти числа
33 а это ответ сервера
– 11 -44
– 55
Но если клиент и сервер работают на машинах с разными архитектурами, например, сервер в системе FreeBSD на SPARC, в которой используется обратный порядок байтов (big-endian), а клиент — в системе Linux на Intel с прямым порядком байтов (little-endian), результат будет неверным:
linux % tcpcli09 206.168.112.96
1 2мы вводим эти числа
3 и сервер дает правильный ответ
– 22 -77потом мы вводим эти числа
– 16777314 и сервер дает неверный ответ
Проблема заключается в том, что два двоичных числа передаются клиентом через сокет в формате с прямым порядком байтов, а сервер интерпретирует их как целые числа, записанные с обратным порядком байтов. Мы видим, что это допустимо для положительных целых чисел, но для отрицательных такой подход не срабатывает (см. упражнение 5.8). Действительно, в подобной ситуации могут возникнуть три проблемы:
1. Различные реализации хранят двоичные числа в различных форматах. Наиболее характерный пример — прямой и обратный порядок байтов, описанный в разделе 3.4.