User Rating: 0 / 5

Star InactiveStar InactiveStar InactiveStar InactiveStar Inactive
 

Поговорили ни о чем, теперь код в студию.

У нас будет очень мало кода, поэтому никакие классы нам не понадобятся, а процедур будет всего две: точка входа с бесконечным циклом принятия соединений и нить с бесконечным циклом приема пакетов с запросами от конкретного соединения.

Т.к. код в основном стырен из гугла (не, не из гугла, а например с code-live.ru), а потом переработан, то комментарии я тоже тырил, думаю это логично :)

все, дальше только код, и комментарии

 



#include <WinSock2.h>

#include <WS2tcpip.h>

// Необходимо, чтобы линковка происходила с DLL-библиотекой 

// Для работы с сокетам

#pragma comment(lib, "Ws2_32.lib")

using std::cerr;

//Это процедура, которая будет "держать" сессию с браузером до потери пулса (соединения)

//она будет запускаться как отдельная нить, т.е. у нас будет много параллельно идущих нитей

void KeepSessionWithClient(int client_socket) {

const int max_client_buffer_size = 1024;

char buf[max_client_buffer_size];//буфер для приема текста запроса

int result = SOCKET_ERROR;

cerr << "start to keep session " << client_socket << "\n";

 

for (;;) {

result = recv(client_socket, buf, max_client_buffer_size, 0);

cerr << "read req " << client_socket << "\n";

std::stringstream response; // сюда будет записываться ответ клиенту

std::stringstream response_body; // тело ответа

 

if (result == SOCKET_ERROR) {

// ошибка получения данных

cerr << "recv failed: " << result << "\n";

closesocket(client_socket);

return;

}

else if (result == 0) {

// соединение закрыто клиентом

cerr << "connection closed...\n";

return;

}

else if (result > 0) {

// Мы знаем размер полученных данных, поэтому ставим метку конца строки

// В буфере запроса.

buf[result] = '\0';

 

// Данные успешно получены

// формируем тело ответа (HTML)

response_body << "<title>Test C++ HTTP Server</title>\n"

<< "<h1>Test page</h1>\n"

<< "<p>This is body of the test page...</p>\n"

<< "<h2>Request headers</h2>\n"

<< "<pre>" << buf << "</pre>\n"

<< "<em><small>Test C++ Http Server</small></em>\n";

 

// Формируем весь ответ вместе с заголовками

response << "HTTP/1.1 200 OK\r\n"

<< "Version: HTTP/1.1\r\n"

<< "Content-Type: text/html; charset=utf-8\r\n"

<< "Content-Length: " << response_body.str().length()

<< "\r\n\r\n"

<< response_body.str();

 

// Отправляем ответ клиенту с помощью функции send

result = send(client_socket, response.str().c_str(),

response.str().length(), 0);

 

if (result == SOCKET_ERROR) {

// произошла ошибка при отправле данных

cerr << "send failed: " << WSAGetLastError() << "\n";

}

 

 

}

}

// Закрывать соединение c клиентом не будем, пока клиент его держит, т.е. никаких таймаутов пока не будет

}

 

int main() {

// служебная структура для хранение информации

// о реализации Windows Sockets

WSADATA wsaData;

 

// старт использования библиотеки сокетов процессом

// (подгружается Ws2_32.dll)

int result = WSAStartup(MAKEWORD(2, 2), &wsaData);

if (result != 0) {

cerr << "WSAStartup failed: " << result << "\n";

return result;

}

 

struct addrinfo* addr = NULL; // структура, хранящая информацию

 // об IP-адресе  слущающего сокета

 // Шаблон для инициализации структуры адреса

struct addrinfo hints;

 

ZeroMemory(&hints, sizeof(hints));

 

// AF_INET определяет, что используется сеть для работы с сокетом

hints.ai_family = AF_INET;

hints.ai_socktype = SOCK_STREAM; // Задаем потоковый тип сокета

hints.ai_protocol = IPPROTO_TCP; // Используем протокол TCP

// Сокет биндится на адрес, чтобы принимать входящие соединения

hints.ai_flags = AI_PASSIVE;

 

// Инициализируем структуру, хранящую адрес сокета - addr.

// HTTP-сервер будет висеть на 8000-м порту локалхоста

result = getaddrinfo(NULL, "8001", &hints, &addr);

// Если инициализация структуры адреса завершилась с ошибкой,

// выведем сообщением об этом и завершим выполнение программы 

if (result != 0) {

cerr << "getaddrinfo failed: " << result << "\n";

WSACleanup(); // выгрузка библиотеки Ws2_32.dll

return 1;//код ошибки везде разный, чтобы было понятно на чем посыпались

}

int listen_socket = socket(addr->ai_family, addr->ai_socktype,

addr->ai_protocol);

// Если создание сокета завершилось с ошибкой, выводим сообщение,

// освобождаем память, выделенную под структуру addr,

// выгружаем dll-библиотеку и закрываем программу

if (listen_socket == INVALID_SOCKET) {

cerr << "Error at socket: " << WSAGetLastError() << "\n";

freeaddrinfo(addr);

WSACleanup();

return 2;

}

// Привязываем сокет к IP-адресу

result = bind(listen_socket, addr->ai_addr, (int)addr->ai_addrlen);

 

// Если привязать адрес к сокету не удалось, то выводим сообщение

// об ошибке, освобождаем память, выделенную под структуру addr.

// и закрываем открытый сокет.

// Выгружаем DLL-библиотеку из памяти и закрываем программу.

if (result == SOCKET_ERROR) {

cerr << "bind failed with error: " << WSAGetLastError() << "\n";

freeaddrinfo(addr);

closesocket(listen_socket);

WSACleanup();

//Не знаю зачем, но сразу выключать не надо, видимо WSACleanup запускает что-то асинхронно

Sleep(2000);

//номера ошибок, чтобы потом знать, где проблема

return 3;

}

 

// Инициализируем слушающий сокет

if (listen(listen_socket, SOMAXCONN) == SOCKET_ERROR) {

cerr << "listen failed with error: " << WSAGetLastError() << "\n";

closesocket(listen_socket);

WSACleanup();

return 4;

}

int client_socket = INVALID_SOCKET;

for (;;) {

// Принимаем входящие соединения

client_socket = accept(listen_socket, NULL, NULL);

if (client_socket == INVALID_SOCKET) {

cerr << "accept failed: " << WSAGetLastError() << "\n";

closesocket(listen_socket);

WSACleanup();

return 1;

}

 

//Запускаем нить для получения запросов

CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)KeepSessionWithClient, (LPVOID)client_socket, NULL, NULL);

 }

 // Убираем за собой

closesocket(listen_socket);

freeaddrinfo(addr);

WSACleanup();

return 0;

}

 



На этом кусок кода, обрамленный банерами, закончен. Пишите в комментарии свои замечания.

 

Авторизуйтесь пожалуйста