Привязка сокета к адресу (bind)




Введение в HTTP

Для начала разберемся, что из себя представляет HTTP. Это текстовый протокол для обмена данными между браузером и веб-сервером.

Пример HTTP-запроса:

GET /page.html HTTP/1.1

Host: site.com

Первая строка передает метод запроса, идентификатор ресурса (URI) и версию HTTP-протокола. Затем перечисляются заголовки запроса, в которых браузер передает имя хоста, поддерживаемые кодировки, cookie и другие служебные параметры. После каждого заголовка ставится символ переноса строки \r\n.

У некоторых запросов есть тело. Когда отправляется форма методом POST, в теле запроса передаются значения полей этой формы.

POST /submit HTTP/1.1

Host site.com

Content-Type: application/x-www-form-urlencoded

 

name=Sergey&last_name=Ivanov&birthday=1990-10-05

Тело запроса отделяется от заголовков одной пустой строкой. Заголовок «Content-Type» говорит серверу, в каком формате закодировано тело запроса. По умолчанию, в HTML-форме данные кодируются методом «application/x-www-form-urlencoded».

Иногда необходимо передать данные в другом формате. Например, при загрузке файлов на сервер, бинарные данные кодируются методом «multipart/form-data».

Сервер обрабатывает запрос клиента и возвращает ответ.

Пример ответа сервера:

HTTP/1.1 200 OK

Host: site.com

Content-Type: text/html; charset=UTF-8

Connection: close

Content-Length: 21

 

<h1>Test page...</h1>

В первой строке ответа передается версия протокола и статус ответа. Для успешных запросов обычно используется статус «200 OK». Если ресурс не найден на сервере, возвращается «404 Not Found».

Тело ответа так же, как и у запроса, отделяется от заголовков одной пустой строкой.

Полная спецификации протокола HTTP описывается в стандарте rfc-2068. По понятным причинам, мы не будем реализовывать все возможности протокола в рамках этого материала. Достаточно реализовать поддержку работы с заголовками запроса и ответа, получение метода запроса, версии протокола и URL-адреса.

Что будет делать сервер?

Сервер будет принимать запросы клиентов, парсить заголовки и тело запроса, и возвращать тестовую HTML-страничку, на которой отображены данные запроса клиента (запрошенный URL, метод запроса, cookie и другие заголовки).

О сокетах

Для работы с сетью на низком уровне традиционно используют сокеты. Сокет — это абстракция, которая позволяет работать с сетевыми ресурсами, как с файлами. Мы можем писать и читать данные из сокета почти так же, как из обычного файла.

В этом материале мы будем работать с виндовой реализацией сокетов, которая находится в заголовочном файле <WinSock2.h>. В Unix-подобных ОС принцип работы с сокетами такой же, только отличается API. Вы можете подробнее почитать о сокетах Беркли, которые используются в GNU/Linux.

Создание сокета

Создадим сокет с помощью функции socket, которая находится в заголовочном файле <WinSock2.h>. Для работы с IP-адресами нам понадобится заголовочный файл <WS2tcpip.h>.

#include <iostream>

#include <sstream>

#include <string>

 

// Для корректной работы freeaddrinfo в MinGW

// Подробнее: https://stackoverflow.com/a/20306451

#define _WIN32_WINNT 0x501

 

#include <WinSock2.h>

#include <WS2tcpip.h>

 

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

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

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

 

using std::cerr;

 

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("127.0.0.1", "8000", &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 1;

}

 

//...

Мы подготовили все данные, которые необходимо для создания сокета и создали сам сокет. Функция socket возвращает целочисленное значение файлового дескриптора, который выделен операционной системой под сокет.

Привязка сокета к адресу (bind)

Следующим шагом, нам необходимо привязать IP-адрес к сокету, чтобы он мог принимать входящие соединения. Для привязки конкретного адреса к сокету используется фукнция bind. Она принимает целочисленный идентификатор файлового дескриптора сокета, адрес (поле ai_addr из структуры addrinfo) и размер адреса в байтах (используется для поддержки IPv6).

// Привязываем сокет к 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();

return 1;

}



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-06-03 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: