У процесі розробки мережевих додатків сервери TCP (Transmission Control Protocol) є основою для встановлення надійних з'єднань між сервером та його клієнтами. У цьому блозі ми розглянемо кроки для створення базового TCP сервера на C++.
Передумови
-
Базове розуміння програмування на C++.
-
Знайомство з концепціями програмування на сокетах.
#define _WINSOCK_DEPRECATED_NO_WARNINGS // Коли ви включаєте заголовки Winsock і використовуєте застарілі функції, компілятор може генерувати попередження, щоб заохотити розробників використовувати новіші альтернативи. Визначення _WINSOCK_DEPRECATED_NO_WARNINGS пригнічує ці попередження
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
int main() {
WSADATA wsa_data{};
SOCKET server_socket{};
SOCKET client_socket{};
sockaddr_in server {};
// Ініціалізує бібліотеку Winsock для включення операцій з сокетами
//MAKEWORD(2, 2): Запитує версію 2.2 для Winsock.
if (WSAStartup(MAKEWORD(2, 2), &wsa_data)!=0) {
std::cout << "Помилка під час ініціалізації сокета..." << std::endl;
}
//Створення серверного сокета
//AF_INET: Визначає IPv4.
//SOCK_STREAM: Вказує на TCP сокет. Для UDP-> SOCK_DGRAM
//IPPROTO_TCP: Використовує протокол TCP.
//Перевірка на INVALID_SOCKET: Переконується, що сокет був успішно створений.
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_socket == INVALID_SOCKET) {
std::cout << "Помилка під час створення серверного сокета" << std::endl;
return 1;
}
//Визначення адреси сервера та прив'язка
//inet_addr: Перетворює рядок "127.0.0.1" (localhost) в мережеву адресу.
//htons: Перетворює порт 8080 в мережевий порядок байтів.
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(8080);
int server_len = sizeof(server);
if (bind(server_socket, (SOCKADDR*)&server, server_len) != 0) {
std::cout << "Не вдалося прив'язати сокет до сервера";
closesocket(server_socket);
WSACleanup();
return 1;
}
//Переводить сокет в режим прослуховування для прийому клієнтських з'єднань.
//SOMAXCONN: Встановлює максимальну кількість очікуючих з'єднань
if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "Не вдалося виконати listen: " << WSAGetLastError() << std::endl;
closesocket(server_socket);
WSACleanup();
return 1;
}
std::cout << "Сервер працює на 127.0.0.1:8080\n";
int bytes = 0;
while (true) {
//accept: Блокує виконання до підключення клієнта, створюючи новий сокет для клієнта.
//Перевірка на INVALID_SOCKET: Обробка помилок підключення
client_socket = accept(server_socket, (SOCKADDR*) &server, &server_len);
if (client_socket == INVALID_SOCKET) {
std::cout << "Прийом з'єднання не вдався: " << WSAGetLastError() << std::endl;
closesocket(server_socket); //Закриття сокета
WSACleanup(); // WSACleanup: Очищає ресурси Winsock.
return 1;
}
//recv: Зчитує дані, надіслані клієнтом, в буфер.
//send: Відправляє відповідь назад клієнту.
char buffer[512] = {};
bytes = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes < 0) {
std::cout << "Не вдалося зчитати запит клієнта";
}
//Повідомлення для клієнта
std::string body = "
Hello world
"; std::string server_message = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n" + body; //надсилаємо клієнту при доступі. int byte_sent = send(client_socket, server_message.c_str(), server_message.size(), 0); if (byte_sent < 0) { std::cout << "Не вдалося надіслати відповідь"; } closesocket(client_socket); } closesocket(server_socket); WSACleanup(); return 0; } ``` ## Основні моменти 1. Цей сервер є базовою реалізацією та обробляє лише один клієнт за раз. 2. Він жорстко налаштований на роботу на `127.0.0.1` (localhost) та порту `8080`. 3. Сервер відповідає фіксованим HTML повідомленням.
Перекладено з: [TCP Server in c/c++ (window)](https://medium.com/@v21it039/tcp-server-in-c-window-fdb40ecd585c)