Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Калюжный. лабы.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
160.08 Кб
Скачать

Программа-клиент

Текст программы-клиента на языке программирования СИ выглядит следующим образом

  1. #include <tiuser.h>

  2. #include <fcntl.h>

  3. #include <stdio.h>

  4. #include <sys/socket.h>

  5. #include <netinet/in.h>

  6. #include <netdb.h>

  7. #include <memory.h>

  8. #define SRV_HOST "delta"

  9. #define SRV_PORT 1234

  10. #define ASK_TXT "What must I do?\n"

  11. #define CONT_TXT "Continue\n"

  12. #define CANC_TXT "Cancel\n"

  13. main () {

  14. int fd;

  15. int flags;

  16. struct t_unitdata *ud;

  17. struct sockaddr_in *p_addr;

  18. struct hostent *hp;

  19. extern int t_errno;

  20. fd = t_open ("/dev/udp", O_RDWR, NULL);

  21. t_bind (fd, NULL, NULL);

  22. ud = (struct t_unitdata *) t_alloc (fd, T_UNITDATA, T_ALL);

  23. memset (ud->addr.buf, '\0', ud->addr.maxlen);

  24. p_addr = (struct sockaddr_in *) ud->addr.buf;

  25. hp = gethostbyname (SRV_HOST);

  26. p_addr->sin_family = AF_INET;

  27. memcpy((char *)&(p_addr->sin_addr),hp->h_addr,hp->h_length);

  28. p_addr->sin_port = SRV_PORT;

  29. ud->addr.len = sizeof(struct sockaddr_in);

  30. while (1) {

  31. strcpy (ud->udata.buf, ASK_TXT);

  32. ud->udata.len = sizeof(ASK_TXT);

  33. t_sndudata (fd, ud);

  34. t_rcvudata (fd, ud, &flags);

  35. write (1, ud->udata.buf, ud->udata.len);

  36. if ( strcmp(ud->udata.buf, CONT_TXT) )

  37. break;

38 };

  1. t_free ((char *) ud, T_UNITDATA);

  2. t_close (fd);

  3. exit (0);

42 }

В строках 8 и 9 описываются константы SRV_HOST и SRV_PORT, определяющие имя удаленного узла, на котором функционирует программа-сервер, и номер порта, к которому привязана транспортная точка сервера. В строках 20 и 21 создается транспортная точка, имеющая не интересующий нас в этой программе транспортный адрес.

В строке 22 выделяется оперативная память под структуру данных типа struct t_unitdata и под три буфера, определяемых полями addr, opt и udata этой структуры (размер памяти, выделяемой под эти буфера, функция t_alloc вычисляет самостоятельно на основе информации о конкретном поставщике транспортных услуг). В строке 25 посредством библиотечной функции gethostbyname транслируется символическое имя удаленного узла (в данном случае "delta"), на котором должен функционировать сервер, в адрес этого узла, размещенный в структуре типа hostent. В строке 27 адрес удаленного узла копируется из структуры типа struct hostent в соответствующее поле структуры типа struct sockaddr_in, которая размещена в буфере ud- >addr. В строках 31 и 32 заполняются поля структуры ud->udata передаваемой серверу информацией и длиной этой информации. В строке 33 с помощью функции t_sndudata посылается запрос серверу. В строке 34 с помощью функции t_rcvudata принимается ответ от сервера. При этом транспортный адрес транспортной точки отправителя ответа (сервера) размещается функцией в ud->addr, а сами данные, составляющие ответ, - в ud->udata. В строке 39 освобождается оперативная память, занимавшаяся структурой типа struct t_unitdata. Строка 40 посредством функции t_close закрывает (удаляет) транспортную точку.

Программа HttpClient

#include <stdlib.h> // stdlib functions

#include <unistd.h> // getopt

#include <stdio.h> // printf, etc

#include <string.h> // strncpy, etc

#include <sys/socket.h> // sockets API

#include <netdb.h> // gethostbyname

#include <errno.h> // errno

// Необходимо для getopt

extern char *optarg;

// Функция main, входная точка программы

int main(int argc /*количество аргументов*/, char* argv[]/*массив аргументов*/)

{

// Текст помощи

const char* usageInfo =

"Использование: ./HttpClient [-u http://host[:port][/page]] [-h]\n"

"\t-u - URL для получения страницы.\n"

"\t-h - эта справка.\n";

// Буффер для строчки с URL

static const int MAX_URL_LEN = 1024;

char urlBuffer[MAX_URL_LEN];

memset(urlBuffer, 0, MAX_URL_LEN);

// Чтение аргументов командной строки в стиле Си

int opt;

while ((opt = getopt(argc, argv, "hu:")) != -1)

{

switch (opt)

{

case 'u':

{

// Использование strncpy вместо strcpy дает защиту от переполнения буфера

strncpy(urlBuffer, optarg, MAX_URL_LEN);

break;

}

case 'h':

default:

{

// Напечатать помощь и выйти

printf(usageInfo);

exit(EXIT_FAILURE);

}

}

}

// если конфигурация после прочтения всех опций неполна - напечатать помощь и выйти

if (strnlen(urlBuffer, MAX_URL_LEN) == 0)

{

printf(usageInfo);

return EXIT_FAILURE;

}

// Данные для разбора URL в стиле Си

char urlHost[MAX_URL_LEN]; // Хост

memset(urlHost, 0, MAX_URL_LEN);

char urlPage[MAX_URL_LEN]; // Адрес страницы

memset(urlPage, 0, MAX_URL_LEN);

strncpy(urlPage, "index.html", MAX_URL_LEN); // Предопределенное значение адреса страницы index.html

int urlPort = 80; // Порт, предопределенное значение 80

// Пропустить http://

char* pBuffer = strstr(urlBuffer, "http://");

if (pBuffer != 0)

pBuffer = urlBuffer + strnlen("http://", MAX_URL_LEN);

else

pBuffer = urlBuffer;

// Разбиение строки в разных кобминациях имени хоста, порта и адреса страницы на сервере

if (sscanf(pBuffer, "%99[^:]:%i/%199[^\n]", urlHost, &urlPort, urlPage) == 3) {}

else if (sscanf(pBuffer, "%99[^/]/%199[^\n]", urlHost, urlPage) == 2) {}

else if (sscanf(pBuffer, "%99[^:]:%i[^\n]", urlHost, &urlPort) == 2) {}

else if (sscanf(pBuffer, "%99[^\n]", urlHost) == 1) {}

else

{

printf("Не удалось разобрать строчку URL");

printf(usageInfo);

return EXIT_FAILURE;

}

// Данные необходимые для соединения и обмена данными

// Структуры для адресов

struct addrinfo hints;

struct addrinfo *result;

struct addrinfo *rp;

// Сокет для обмена данными

int sfd; // socket file descriptor

// код ошибки

int s;

static const int BUF_SIZE = 256*1024;

// Буфер памяти для сетевого обмена, фиксированной длинны на стеке

char buf[BUF_SIZE];

// Инициализация структур для получения ip адреса по имени хоста

memset(&hints, 0, sizeof(struct addrinfo));

hints.ai_family = AF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

snprintf(buf, BUF_SIZE, "%d", urlPort);

// Получение набора адресов соответствующих имени хоста urlHost

s = getaddrinfo(urlHost, buf, &hints, &result);

if (s != 0)

{

fprintf(stderr, "Ошибка getaddrinfo: %s\n", gai_strerror(s));

perror(strerror(errno));

return EXIT_FAILURE;

}

// getaddrinfo() Возвращает набор структур с адресами

// Будем пробовать все подряд пока не соединимся

for (rp = result; rp != NULL; rp = rp->ai_next)

{

// Создание TCP сокета

sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);

if (sfd == -1)

continue;

// Попытка соединения, выход из цикла если удачно

if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)

break;

// Закрытие сокета

close(sfd);

}

// Если ни один адрес не подошел

if (rp == NULL)

{

fprintf(stderr, "Неудается установить соединение\n");

return EXIT_FAILURE;

}

// Освобождение структур содержащих разрезолвленные адреса

freeaddrinfo(result);

// Подготовка простейшего HTTP запроса

char requestPattern[] = "GET /%s HTTP/1.1\r\n"

"Host: %s\r\n"

"Connection: close\r\n"

"Accept: text/html;q=1.0\r\n"

"Accept-Language: en-US,en;q=1.0\r\n\r\n";

snprintf(buf, BUF_SIZE, requestPattern, urlPage, urlHost);

// Отправка запроса

// total - количество байт к отправке

int total = strnlen(buf, BUF_SIZE);

// sent - общее количество отправленных байт

int sent = 0;

// bytes - количество байт отправленных за последний заход

int bytes;

do

{

// попытка записи в сокет с текущего места и до конца

bytes = write(sfd, buf + sent, total - sent);

if (bytes < 0)

{

printf("Ошибка при записи в сокет");

perror(strerror(errno));

return EXIT_FAILURE;

}

if (bytes == 0)

break;

sent += bytes;

} while (sent < total); // Отсылка данных пока есть, что посылать

// Очистка буфера сетевого обмена

memset(buf, 0, BUF_SIZE);

// Максимальный размер получаемых данных равен размеру буфера

total = BUF_SIZE;

// Общее количество полученных байт

int received = 0;

do

{

// Чтение в буфер поэтапно

bytes = read(sfd, buf + received, total - received);

if (bytes < 0)

{

printf("Ошибка при чтении из сокета");

perror(strerror(errno));

return EXIT_FAILURE;

}

// Получение ноля байт говорит о том, что противополжная сторона закрыла соединение

if (bytes == 0)

break;

received += bytes;

} while (received < total); // Получение данных пока есть место в буфере

// Если буфер закончился и нехватает места на терминирующий ноль - случилось переполнение

if (received == total)

{

printf("Ошибка при чтении из сокета - буфер переполнен");

exit(EXIT_FAILURE);

}

// Нультерминируем буфер после чтения

buf[received] = 0;

// Закрываем сокет

close(sfd);

// Выводим ответ сервера

printf("Ответ:\n%s\n", buf);

// Успешное завершение

return EXIT_SUCCESS;

}

Примечание. Данная клиентская программа по заданному URL определяет у Web-сервера его IP-адреса и по первому адресу, содержащемуся в полученной структуре, запрашивает у сервера его главную страницу. В программе используется функция perror() которая записывает в переменную errno код ошибки.

Приложение А.

Основные функции библиотеки Sockets API.

        1. accept()

Принимает входные подключения на слушающем сокете.

Прототип

#include <sys/types.h>

#include <sys/socket.h>

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

Описание

accept() вызываетcя чтобы получить новый дескриптор сокета для последующего общения с только что подключённым клиентом.

Возвращаемый accept()-ом дескриптор определяет уже открытый и подключённый к

.

Возвращаемое значение

accept() возвращает дескриптор только что подключённого сокета, или -1 при ошибке, при этом соответствующим образом установив errno.

Пример

struct sockaddr_storage their_addr; socklen_t addr_size;

struct addrinfo hints, *res; int sockfd, new_fd;

// сначала заполняем адресные структуры с помощью getaddrinfo(): memset(&hints, 0, sizeof hints);

hints.ai_family = AF_UNSPEC; // использовать либо IPv4 либо IPv6

hints.ai_socktype = SOCK_STREAM;

hints.ai_flags = AI_PASSIVE; // заполнить мой IP для меня getaddrinfo(NULL, MYPORT, &hints, &res);

// создать сокет, связать и слушать:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); bind(sockfd, res->ai_addr, res->ai_addrlen);

listen(sockfd, BACKLOG);

// теперь принять входящие подключения: addr_size = sizeof their_addr;

new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

// можно беседовать по дескриптору сокета new_fd!

        1. bind()

Связывает сокет с IP адресом и номером порта.