Крисс Касперски - Самоучитель игры на winsock. ВведениеСокеты
Скачать 188.88 Kb.
|
Законченные реализации Ниже приведено четыре подробно комментированных исходных текста, реа лизующих простых TCP и UDP эхо сервера и TCP и UDP клиентов. (Эхо сервер просто возвращает клиенту, полученные от него данные). Для их компиляции с помощью Microsoft Visual C++ достаточно отдать команду: "cl.exe имя_файла.cpp ws2_32.lib". Проверка работоспособности TCP сервера: запустите TCP сервер и наберите в командной строке Windows "telnet.exe 127.0.0.1 666", где 127.0.0.1 обозначает локальный адрес вашего узла (это специально зарезервированный для этой цели адрес и он выглядит одинаково для всех узлов), а 666 – номер порта на который Крис Касперски 1 14 4 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K "сел" сервер. Если все работает успешно, то telnet установит соединение и на экра не появится приветствие "Hello, Sailor!". Теперь можно набирать на клавиатуре некоторый текст и получать его назад от сервера. Проверка работоспособности TCP клиента: запустите TCP сервер и затем одну или несколько копий клиента. В каждом из них можно набирать некоторый текст на клавиатуре и после нажатия на Enter получать его обратно от сервера. Проверка работоспособности UDP сервера и клиента: запустите UDP сервер и одну или несколько копий клиента – в каждой из них можно набирать на клавиа туре некоторые данные и получать их обратно от сервера. Внимание: работая с серверными приложениями, вы (если не предпримите дополнительных мер) предоставляете возможность воспользоваться ими каждому абоненту Интернет (если в момент работы сервера вы подключены к Интернет). Проблема в том, что ошибки реализации, в особенности перепол няющиеся буфера, могут позволить удаленному злоумышленнику выполнить на вашей машине любой код, со всеми вытекающими от сюда последствиями. Будьте очень внимательны, а еще лучше, не входите в Интернет, пока не будете полностью уверенны, что сервера отлажены и не содержат ошибок! Пример реализации TCP эхо сервера // Пример простого TCP эхо сервера #include #include // Wincosk2.h должен быть раньше windows! #include #define MY_PORT 666 // Порт, который слушает сервер // макрос для печати количества активных пользователей #define PRINTNUSERS if (nclients) printf("%d user on line\n",nclients);else printf("No User on line\n"); // прототип функции, обслуживающий подключившихся пользователей DWORD WINAPI SexToClient(LPVOID client_socket); // глобальная переменная – количество активных пользователей int nclients = 0; int main(int argc, char* argv[]) { char buff[1024]; // Буфер для различных нужд printf("TCP SERVER DEMO\n"); // Шаг 1 Инициализация Библиотеки Сокетов // Т. к. возвращенная функцией информация не используется // ей передается указатель на рабочий буфер, преобразуемый к указателю // на структуру WSADATA. // Такой прием позволяет сэкономить одну переменную, однако, буфер // должен быть не менее полкилобайта размером (структура WSADATA // занимает 400 байт) Крис Касперски 1 15 5 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K 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(); // Деиницилизация библиотеки Winsock return 1; } // Шаг 3 связывание сокета с локальным адресом sockaddr_in local_addr; local_addr.sin_family=AF_INET; local_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 ожидание подключений // размер очереди – 0x100 if (listen(mysocket, 0x100)) { // Ошибка printf("Error listen %d\n",WSAGetLastError()); closesocket(mysocket); WSACleanup(); return 1; } printf("Ожидание подключений…\n"); // Шаг 5 извлекаем сообщение из очереди SOCKET client_socket; // сокет для клиента sockaddr_in client_addr; // адрес клиента (заполняется системой) Крис Касперски 1 16 6 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K // функции accept необходимо передать размер структуры int client_addr_size=sizeof(client_addr); // цикл извлечения запросов на подключение из очереди while((client_socket=accept(mysocket, (sockaddr *) &client_addr, &client_addr_size))) { nclients++; // увеличиваем счетчик подключившихся клиентов // пытаемся получить имя хоста HOSTENT *hst; hst=gethostbyaddr((char *) &client_addr.sin_addr.s_addr,4,AF_INET); // вывод сведений о клиенте printf("+%s [%s] new connect!\n", (hst)?hst >h_name:"",inet_ntoa(client_addr.sin_addr)); PRINTNUSERS // Вызов нового потока для обслужвания клиента // Да, для этого рекомендуется использовать _beginthreadex // но, поскольку никаких вызов функций стандартной Си библиотеки // поток не делает, можно обойтись и CreateThread DWORD thID; CreateThread(NULL,NULL,SexToClient,&client_socket,NULL,&thID); } return 0; } // Эта функция создается в отдельном потоке // и обсуживает очередного подключившегося клиента независимо от остальных DWORD WINAPI SexToClient(LPVOID client_socket) { SOCKET my_sock; my_sock=((SOCKET *) client_socket)[0]; char buff[20*1024]; #define sHELLO "Hello, Sailor\r\n" // отправляем клиенту приветствие send(my_sock,sHELLO,sizeof(sHELLO),0); // цикл эхо сервера: прием строки от клиента и возвращение ее клиенту while( (int bytes_recv=recv(my_sock,&buff[0],sizeof(buff),0)) && bytes_recv !=SOCKET_ERROR) send(my_sock,&buff[0],bytes_recv,0); // если мы здесь, то произошел выход из цикла по причине // возращения функцией recv ошибки – соединение с клиентом разорвано nclients ; // уменьшаем счетчик активных клиентов printf(" disconnect\n"); PRINTNUSERS // закрываем сокет closesocket(my_sock); return 0; } Крис Касперски 1 17 7 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K Пример реализации TCP клиента // Пример простого TCP клиента #include #include #include #include #define PORT 666 #define SERVERADDR "127.0.0.1" int main(int argc, char* argv[]) { char buff[1024]; printf("TCP DEMO CLIENT\n"); // Шаг 1 инициализация библиотеки Winsock if (WSAStartup(0x202,(WSADATA *)&buff[0])) { printf("WSAStart error %d\n",WSAGetLastError()); return 1; } // Шаг 2 создание сокета SOCKET my_sock; my_sock=socket(AF_INET,SOCK_STREAM,0); if (my_sock<0) { printf("Socket() error %d\n",WSAGetLastError()); return 1; } // Шаг 3 установка соединения // заполнение структуры sockaddr_in – указание адреса и порта сервера sockaddr_in dest_addr; dest_addr.sin_family=AF_INET; dest_addr.sin_port=htons(PORT); HOSTENT *hst; // преобразование IP адреса из символьного в сетевой формат if (inet_addr(SERVERADDR)!=INADDR_NONE) dest_addr.sin_addr.s_addr=inet_addr(SERVERADDR); else // попытка получить IP адрес по доменному имени сервера if (hst=gethostbyname(SERVERADDR)) // hst >h_addr_list содержит не массив адресов, // а массив указателей на адреса ((unsigned long *)&dest_addr.sin_addr)[0]= ((unsigned long **)hst >h_addr_list)[0][0]; else { printf("Invalid address %s\n",SERVERADDR); closesocket(my_sock); WSACleanup(); return 1; } Крис Касперски 1 18 8 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K // адрес сервера получен – пытаемся установить соединение if (connect(my_sock,(sockaddr *)&dest_addr,sizeof(dest_addr))) { printf("Connect error %d\n",WSAGetLastError()); return 1; } printf("Соединение с %s успешно установлено\n\ Type quit for quit\n\n",SERVERADDR); // Шаг 4 чтение и передача сообщений int nsize; while((nsize=recv(my_sock,&buff[0],sizeof(buff) 1,0))!=SOCKET_ERROR) { // ставим завершающий ноль в конце строки buff[nsize]=0; // выводим на экран printf("S=>C:%s",buff); // читаем пользовательский ввод с клавиатуры printf("S<=C:"); fgets(&buff[0],sizeof(buff) 1,stdin); // проверка на "quit" if (!strcmp(&buff[0],"quit\n")) { // Корректный выход printf("Exit..."); closesocket(my_sock); WSACleanup(); return 0; } // передаем строку клиента серверу send(my_sock,&buff[0],nsize,0); } printf("Recv error %d\n",WSAGetLastError()); closesocket(my_sock); WSACleanup(); return 1; } Пример реализации UDP сервера // Пример простого UDP эхо сервера #include #include #define PORT 666 // порт сервера #define sHELLO "Hello, %s [%s] Sailor\n" Крис Касперски 1 19 9 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K int main(int argc, char* argv[]) { char buff[1024]; printf("UDP DEMO echo Server\n"); // Шаг 1 подключение библиотеки if (WSAStartup(0x202,(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()); // Определяем 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)); Крис Касперски 2 20 0 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K // добавление завершающего нуля 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 клиента // пример простого UDP клиента #include #include #include #include #define PORT 666 #define SERVERADDR "127.0.0.1" int main(int argc, char* argv[]) { char buff[10*1014]; printf("UDP DEMO Client\nType quit to quit\n"); // Шаг 1 иницилизация библиотеки Winsocks if (WSAStartup(0x202,(WSADATA *)&buff[0])) { printf("WSAStartup error: %d\n",WSAGetLastError()); return 1; } // Шаг 2 открытие сокета SOCKET my_sock=socket(AF_INET, SOCK_DGRAM, 0); if (my_sock==INVALID_SOCKET) { printf("socket() error: %d\n",WSAGetLastError()); WSACleanup(); return 1; } // Шаг 3 обмен сообщений с сервером HOSTENT *hst; sockaddr_in dest_addr; dest_addr.sin_family=AF_INET; dest_addr.sin_port=htons(PORT); Крис Касперски 2 21 1 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K // определение IP адреса узла if (inet_addr(SERVERADDR)) dest_addr.sin_addr.s_addr=inet_addr(SERVERADDR); else if (hst=gethostbyname(SERVERADDR)) dest_addr.sin_addr.s_addr=((unsigned long **) hst >h_addr_list)[0][0]; else { printf("Unknown host: %d\n",WSAGetLastError()); closesocket(my_sock); WSACleanup(); return 1; } while(1) { // чтение сообщения с клавиатуры printf("S<=C:");fgets(&buff[0],sizeof(buff) 1,stdin); if (!strcmp(&buff[0],"quit\n")) break; // Передача сообщений на сервер sendto(my_sock,&buff[0],strlen(&buff[0]),0, (sockaddr *) &dest_addr,sizeof(dest_addr)); // Прием сообщения с сервера sockaddr_in server_addr; int server_addr_size=sizeof(server_addr); int n=recvfrom(my_sock,&buff[0],sizeof(buff) 1,0, (sockaddr *) &server_addr, &server_addr_size); if (n==SOCKET_ERROR) { printf("recvfrom() error: %d\n",WSAGetLastError()); closesocket(my_sock); WSACleanup(); return 1; } buff[n]=0; // Вывод принятого с сервера сообщения на экран printf("S=>C:%s",&buff[0]); } // Шаг последний выход closesocket(my_sock); WSACleanup(); return 0; } Крис Касперски 2 22 2 С Са ам мо оууч чи итте ел ль ь и иггр ры ы н на а W WIIN NS SO OC CK K |