Семинары (1171138), страница 11
Текст из файла (страница 11)
Вызов recvfrom не требует предварительного задания адресапередающего узла - функция принимает все пакеты, приходящие на заданный UDP-порт со всех IP-адресов ипортов. Напротив, отвечать отправителю следует на тот же самый порт откуда пришло сообщение. Посколькуфункция recvfrom заносит IP-адрес и номер порта клиента после получения от него сообщения, программистуфактически ничего не нужно делать - только передать sendto тот же самый указатель на структуру sockaddr,который был ранее передан функции recvfrem, получившей сообщение от клиента.Во всем остальном обе пары функций полностью идентичны и работают с теми самыми флагами - MSG_PEEKи MSG_OOB.Все четыре функции при возникновении ошибки возвращают значение SOCKET_ERROR (== -1).// Пример простого UDP-эхо сервера#include <stdio.h>#include <winsock2.h>3#include "windows.h"#include <iostream>#pragma comment(lib, "Ws2_32.lib")#define PORT 777 // порт сервера#define sHELLO "Hello, %s [%s] Sailor\n"using namespace std;int main(){char buff[1024];printf("UDP DEMO echo-Server\n");// Шаг 1 - подключение библиотекиif (WSAStartup(0x0202,(WSADATA *) &buff[0])){printf("WSAStartup error: %d\n", WSAGetLastError());return -1;}// Шаг 2 - создание сокетаSOCKET my_sock;my_sock = socket(AF_INET,SOCK_DGRAM,0);if (my_sock == INVALID_SOCKET){printf("Socket() error: %d\n", WSAGetLastError());WSACleanup();return -1;}// Шаг 3 - связывание сокета с локальным адресомsockaddr_in local_addr;local_addr.sin_family = AF_INET;local_addr.sin_addr.s_addr = INADDR_ANY;local_addr.sin_port = htons(PORT);if (bind(my_sock, (sockaddr *)&local_addr,sizeof(local_addr))){printf("bind error: %d\n", WSAGetLastError());closesocket(my_sock);WSACleanup();return -1;}// Шаг 4 обработка пакетов, присланных клиентамиwhile (1){sockaddr_in client_addr;int client_addr_size = sizeof(client_addr);int bsize = recvfrom(my_sock,&buff[0],sizeof(buff)-1,0,(sockaddr *)&client_addr, &client_addr_size);if (bsize == SOCKET_ERROR)printf("recvfrom() error: %d\n", WSAGetLastError());4// Определяем IP-адрес клиента и прочие атрибутыHOSTENT *hst;hst = gethostbyaddr((char *)&client_addr.sin_addr, 4,AF_INET);printf("+%s [%s:%d] new DATAGRAM!\n",(hst) ? hst->h_name : "Unknown host",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));// добавление завершающего нуляbuff[bsize] = 0;// Вывод на экранprintf("C=>S:%s\n", &buff[0]);// посылка датаграммы клиентуsendto(my_sock, &buff[0], bsize, 0,(sockaddr *)&client_addr, sizeof(client_addr));}return 0;}Скомпилировать udp эхо-сервер и запустить.Результат выполнения5Задание 1:Структура приложения-сервера TCPWSAStartupsocketbindlistenacceptsendrecvclosesocketclosesocketWSACleanupСтруктура приложения-клиента TCPWSAStartupsocketconnectsendrecvclosesocketWSACleanupИзменив исходные коды UDP сервера и примеров 3 и 4, написать TCP эхо сервер.
Вприложении сервера вынести работу с клиентом в отдельную функцию. Продемонстрироватьсовместную работу клиента с лабораторной работы №10 и написанного сервера.Исходный код TCP сервера6Результат выполнения7Контрольные вопросы1.2.3.4.5.6.7.8.Понятие сокета?Типы сокетов?Создание сокета?Понятие IP адреса?Связывание сокета?Создание канала связи?Передача данных?UDP эхо-сервер?СПИСОК ЛИТЕРАТУРЫ1. Ватолин Д. С. Алгоритмы сжатия изображений. Методическое пособие: М.:Издательский отдел факультета Вычислительной Математики и Кибернетики МГУим. М.В.Ломоносова, 1999 г.
— 76 с.2. Стивенс У., UNIX: Разработка сетевых приложений. - СПб.: Питер, 20043. Шмидт Д., Хьюстон С. Программирование сетевых приложений на C++. Том1. —Бином-Пресс, 2003. — С. 304.4. Дейтел Х. М. Как программировать на С++: Пер. с англ. – М.: ЗАО «ИздательствоБИНОМ», 2000 г.
– 1024 с.: ил.5. Страуструп Б. Язык программирования С++. Специальное издание: ++: Пер. с англ. –М.: ЗАО «Издательство БИНОМ», 2008 г. – 1104 с. :ил.6. Шилдт Г. Полный справочник по С++: ++: Пер. с англ. – М.: Изд-во Вильямс, 2007 г.– 800 с.: ил.7. Шилдт Г. С++: Базовый курс: ++: Пер. с англ. – М.: Изд-во Вильямс, 2008 г.
– 624 с.:ил.8датаОтчет по лабораторной работе №11«Основы C++, понятие callback функции и потока.»ОценкаБонус заподпись(max 5)сложностьЦели работы:Изучение принципов функционированияпрограммированием сокетов и потоков.сетевыхсоединений,ознакомлениесЗадачи работы:-ознакомление с понятием сетевого соединения и сокетов- понятие процесса и потока-разработка клиент серверного приложение «чат»Краткий конспект теоретической части (ответы на контрольные вопросы)Понятие callback-фукнции?_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________Создание и вызов callback-функции?______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________Понятие процесса и потока?____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________Создание потока?__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________Клиент-сервер UDP/TCP?______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________Текстовый чат?_______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________1Основы C++, понятие callback функции и потока.Пример 1: Использование callback-функцииCallback (обратный вызов) — передача исполняемого кода в качестве одного из параметров другогокода.
Обратный вызов позволяет в функции исполнять код, который задается в аргументах при её вызове.#include <stdio.h>#include <iostream>typedef unsigned char BYTE;using namespace std;typedef int (__stdcall *CompareFunction)(const BYTE*, constBYTE*);void__stdcallvoid __stdcallBubblesort(BYTE* array,int size,int elem_size,CompareFunction cmpFunc);Bubblesort(BYTE* array,int size,int elem_size,CompareFunction cmpFunc){for(int i=0; i < size; i++){for(int j=0; j < size-1; j++){// make the callback to the comparison functionif(1 == (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size)){// the two compared elements must be interchangedint* temp = new int[elem_size];memcpy(temp, array+j*elem_size, elem_size);memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);memcpy(array+(j+1)*elem_size, temp, elem_size);delete [] temp;}}}}int __stdcallCompareInts(const BYTE* velem1, const BYTE*velem2){int elem1 = *(int*)velem1;2int elem2 = *(int*)velem2;if(elem1 < elem2)return -1;if(elem1 > elem2)return 1;return 0;}int __stdcall CompareStrings(const BYTE* velem1, const BYTE*velem2){const char* elem1 = (char*)velem1;const char* elem2 = (char*)velem2;return strcmp(elem1, elem2);}int main(int argc, char* argv[]){int i;int array[] = {5432, 4321, 3210, 2109, 1098};cout << "Before sorting ints with Bubblesort\n";for(i=0; i < 5; i++)cout << array[i] << '\n';Bubblesort((BYTE*)array, 5, sizeof(array[0]), &CompareInts);cout << "After the sorting\n";for(i=0; i < 5; i++)cout << array[i] << '\n';const char str[5][10] = {"estella","danielle","crissy","bo","angie"};cout << "Before sorting strings with Bubblesort\n";for(i=0; i < 5; i++)cout << str[i] << '\n';Bubblesort((BYTE*)str, 5, 10, &CompareStrings);cout << "After the sorting\n";for(i=0; i < 5; i++)cout << str[i] << '\n';return 0;}3Результат выполненияПример 2: Понятие потока:При запуске программы ОС всегда создает один поток – главный.
Именно в нем программа начинает своевыполнение. В зависимости от типа программы и настроек компилятора в главном потоке можетвыполняться функция main, WinMain, _tmain или функция, заданная пользователем. Главный поток живетдо тех пор, пока самая первая функция в стеке не завершила свое выполнение. В то ж время, главнаяфункция может создавать дополнительные потоки, которые будут выполняться одновременно в основнымпотоком. Для нашей задачи нужно, чтобы главная функция сервера main создавала отдельный поток дляработы с каждым подключившимся клиентом.CreateThread.HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);Параметры функции CreateThread.LpThreadAttributes - является указателем на структуру LPSECURITY_ATTRIBUTES. Для присвоенияатрибутов защиты по умолчанию, передавайте в этом параметре NULL.DwStackSize - параметр определяет размер стека, выделяемый для потока из общего адресного пространствапроцесса.
При передаче 0 - размер устанавливается в значение по умолчанию.LpStartAddress - указатель на адрес входной функции потока.LpParameter - параметр, который будет передан внутрь функции потока.DwCreationFlags - принимает одно из двух значений: 0 - исполнение начинается немедленно, илиCREATE_SUSPENDED - исполнение приостанавливается до последующих указаний.LpThreadId - Адрес переменной типа DWORD в который функция возвращает идентификатор, приписанныйсистемой новому потоку.В случае серверного приложения из задания 1 в callback функцию можно вынести работа сервера склиентом, после чего вызывать эту функцию в отдельном потоке с помощью функции CreateThread.// Пример простого – эхо сервера#include <stdio.h>#include <winsock2.h> // Wincosk2.h должен быть раньше windows!#include <windows.h>#define MY_PORT 777 // Порт, который слушает сервер// макрос для печати количества активных пользователей#define PRINTNUSERS if (nclients) printf("%d user online\n",nclients);else printf("No User on line\n");4// прототип функции, обслуживающий подключившихся пользователейDWORD WINAPI WorkWithClient(LPVOID client_socket);// глобальная переменная – количество активных пользователейint nclients = 0;int index=-1; // индекс для массива сокетовSOCKET sockAr[128];void sockAr_init(){for(int i=0;i<127;i++)sockAr[i]=0;}int main(int argc, char* argv[]){char buff[1024]; // Буфер для различных нуждprintf("TCP SERVER DEMO\n");// Шаг 1 - Инициализация Библиотеки Сокетов// Т.к.
возвращенная функцией информация не используется// ей передается указатель на рабочий буфер, преобразуемый куказателю// на структуру WSADATA.// Такой прием позволяет сэкономить одну переменную, однако,буфер// должен быть не менее полкилобайта размером (структура WSADATA// занимает 400 байт)if (WSAStartup(0x0202,(WSADATA *) &buff[0])){// Ошибка!printf("Error WSAStartup %d\n",WSAGetLastError());return -1;}// Шаг 2 - создание сокетаSOCKET mysocket;// AF_INET - сокет Интернета// SOCK_STREAM - потоковый сокет (с установкой соединения)// 0 - по умолчанию выбирается TCP протоколif ((mysocket=socket(AF_INET,SOCK_STREAM,0))<0){// Ошибка!printf("Error socket %d\n",WSAGetLastError());WSACleanup(); // Деиницилизация библиотеки Winsockreturn -1;}// Шаг 3 связывание сокета с локальным адресомsockaddr_in local_addr;local_addr.sin_family=AF_INET;5local_addr.sin_port=htons(MY_PORT); // не забываем о сетевомпорядке!!!local_addr.sin_addr.s_addr=0; // сервер принимает подключения// на все свои IP-адреса// вызываем bind для связыванияif (bind(mysocket,(sockaddr *) &local_addr, sizeof(local_addr))){// Ошибкаprintf("Error bind %d\n",WSAGetLastError());closesocket(mysocket); // закрываем сокет!WSACleanup();return -1;}// Шаг 4 ожидание подключений// размер очереди – 0x100if (listen(mysocket, 0x100)){// Ошибкаprintf("Error listen %d\n",WSAGetLastError());closesocket(mysocket);WSACleanup();return -1;}printf("Waiting for connections...