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. Разрываем соединение.
Задача:
Реализовать клиент-серверное приложение, использующее протокол 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 для посылки/приема эхо сообщений.
Решение:
Программа 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; } |
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.