Методическое пособие по выполнению практических работ в среде ОС Unix с вводной теоретической частью

Страницы работы

Фрагмент текста работы

FD_SET(int fd, fd_set *set) - добавляет дескриптор fd в множество set

•  FD_CLR(int fd, fd_set *set) - удаляет дескриптор fd из множества set

•  FD_ISSET(int fd, fd_set *set) - проверяет, содержится ли дескриптор fd в множестве set

Если хотя бы один сокет готов к выполнению заданной операции, select возвращает ненулевое значение, а все дескрипторы, которые привели к «срабатыванию» функции, записываются в соответствующие множества. Это позволяет проанализировать содержащиеся в множествах дескрипторы и выполнить над ними необходимые действия. Например, вызвать соответствующий обработчик события. Если сработал таймаут, select возвращает ноль, а в случае ошибки -1. Расширенный код записывается в errno.

Программы, использующие мультиплексирование ввода/вывода с помощью select, могут выглядеть весьма запутанными. Если в случае с fork мы строим логику программы, как будто клиент всего один, то здесь программа вынуждена отслеживать дескрипторы всех клиентов и работать с ними попеременно. Как правило, для каждого подключенного клиента хранится состояние диалога с ним.

Работа по стандартным протоколам

Сокеты могут использоваться при написании приложений, работающих по протоколам прикладного уровня Internet (HTTP, FTP, SMTP и т. д.). При этом взаимодействие клиента и сервера происходит по той же самой схеме, что и взаимодействие эхо-клиента и эхо-сервера в нашем примере. Разница в том, что данные, которыми обмениваются клиент и сервер, интерпретируются в соответствии с предписаниями соответствующего протокола.

Например, веб-сервер может работать по следующему алгоритму.

1.  Создаём слушающий сокет и привязываем его к 80-му порту (стандартный порт для HTTP-сервера).

2.  Принимаем очередной запрос на соединение.

3.  Читаем HTTP-запрос от клиента (он имеет стандартный формат и описан в RFC2616).

4.  Обрабатываем запрос и отправляем клиенту ответ, который также имеет стандартный формат.

5.  Разрываем соединение.

Веб-браузер, который является клиентом по отношению к веб-серверу, может использовать похожий алгоритм.

1.  Соединяемся с сервером по заданному адресу.

2.  Отправляем ему HTTP-запрос.

3.  Получаем и обрабатываем ответ сервера (например, форматируем и выводим на экран полученную HTML-страницу).

4.  Разрываем соединение.

Практические занятия по использованию Socket API

Практическое занятие. Простой TCP эхо-клиент и эхо-сервер

Задача:

Реализовать клиент-серверное приложение, использующее протокол TCP для посылки/приема эхо сообщений.

Решение:

Эхо-клиент посылает сообщение "Hello there!" и выводит на экран ответ сервера. Его код приведён в листинге 1. Эхо-сервер читает всё, что передаёт ему клиент, а затем просто отправляет полученные данные обратно. Его код содержится в листинге 2.

Листинг 1. Эхо-клиент.


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

char message[] = "Hello there!\n";

char buf[sizeof(message)];

int main()

{

int sock;

struct sockaddr_in addr;

sock = socket(AF_INET, SOCK_STREAM, 0);

if(sock < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425); // или любой другой порт...

addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("connect");

exit(2);

}

send(sock, message, sizeof(message), 0);

recv(sock, buf, sizeof(message), 0);

printf(buf);

close(sock);

return 0;

}


Листинг 2. Эхо-сервер.


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main()

{

int sock, listener;

struct sockaddr_in addr;

char buf[1024];

int bytes_read;

listener = socket(AF_INET, SOCK_STREAM, 0);

if(listener < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("bind");

exit(2);

}

listen(listener, 1);

while(1)

{

sock = accept(listener, NULL, NULL);

if(sock < 0)

{

perror("accept");

exit(3);

}

while(1)

{

bytes_read = recv(sock, buf, 1024, 0);

if(bytes_read <= 0) break;

send(sock, buf, bytes_read, 0);

}

close(sock);

}

return 0;

}


Практическое занятие. Датаграммный эхо-клиент и сервер, протокол UDP

Задача:

Реализовать клиент-серверное приложение, использующее протокол UDP для посылки/приема эхо сообщений.

Решение:

Программа sender (листинг 3) отправляет сообщения "Hello there!" и "Bye bye!". Программа receiver (листинг 4) получает их и печатает на экране. Программа sender демонстрирует применение как обычного, так и присоединённого сокета, а receiver использует обычный.

Листинг 3. Программа sender.


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

char msg1[] = "Hello there!\n";

char msg2[] = "Bye bye!\n";

int main()

{

int sock;

struct sockaddr_in addr;

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

sendto(sock, msg1, sizeof(msg1), 0,

(struct sockaddr *)&addr, sizeof(addr));

connect(sock, (struct sockaddr *)&addr, sizeof(addr));

send(sock, msg2, sizeof(msg2), 0);

close(sock);

return 0;

}


Листинг 4. Программа receiver.


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

int main()

{

int sock;

struct sockaddr_in addr;

char buf[1024];

int bytes_read;

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("bind");

exit(2);

}

while(1)

{

bytes_read = recvfrom(sock, buf, 1024, 0, NULL, NULL);

buf[bytes_read] = '\0';

printf(buf);

}

return 0;

}


Практическое занятие. Использование параллельных процессов для параллельной обработки запросов

Задача:

Реализовать параллельную обработку на стороне сервера диалогов с подключенными клиентами с помощью множества параллельных процессов.

Решение:

Чтобы проиллюстрировать эту методику можно реализовать эхо-сервер с использованием функции fork. Результат приведен в листинге 5. Эта программа написана на языке С. Она создает новый процесс для каждого нового подключения клиента.

Листинг 5. Эхо-сервер, fork


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main()

{

int sock, listener;

struct sockaddr_in addr;

char buf[1024];

int bytes_read;

listener = socket(AF_INET, SOCK_STREAM, 0);

if(listener < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = INADDR_ANY;

if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("bind");

exit(2);

}

listen(listener, 1);

while(1)

{

sock = accept(listener, NULL, NULL);

if(sock < 0)

{

perror("accept");

exit(3);

}

switch(fork())

{

case -1:

perror("fork");

break;

case 0:

close(listener);

while(1)

{

bytes_read = recv(sock, buf, 1024, 0);

if(bytes_read <= 0) break;

send(sock, buf, bytes_read, 0);

}

close(sock);

_exit(0);

default:

close(sock);

}

}

close(listener);

return 0;

}


Практическое занятие. Использование мультиплексирования ввода/вывода для параллельной

Похожие материалы

Информация о работе

Тип:
Методические указания и пособия
Размер файла:
186 Kb
Скачали:
0