#include "stdafx.h" #include #include /* Эхо-сервер. Все символы, которые приходят от клиента, отправляются ему обратно. В командной строке можно указать порт сервера. По умолчанию - 12345. При компиляции в VisualStudio возможно потребуется библиотека wsock32.lib. */ #define DEFPORTNUM 12345 #define BUFSIZE 256 #define ADDRLEN (sizeof(struct sockaddr_in)+16) // Структура, описывающая контекст клиента typedef struct t_client_data { SOCKET sock; char buf[BUFSIZE]; DWORD len; OVERLAPPED ov; } client_data; // Порт оповещений о завершении AIO HANDLE iocomp; // Асинхронный приём нового запроса от клиента client_data* try_accept(SOCKET master_sock); // Асинхронное чтение данных от клиента void try_read(client_data *data); /** * Точка входа в программу * @param argc Кол-во параметров в командной строке * @param argv Массив параметров командной строки */ int _tmain(int argc, _TCHAR* argv[]) { u_short portnum = DEFPORTNUM; if (argc > 1) { sscanf(argv[1], "%hd", &portnum); } // Создание порта оповещений о завершении AIO iocomp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (iocomp == INVALID_HANDLE_VALUE) { fprintf(stderr, "create...port() failed: %d\n", GetLastError()); exit(1); } // Инициалиализация WinSock WSADATA wsadata = {0}; if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) { fprintf(stderr, "wsastartup failed: %d\n", WSAGetLastError()); exit(1); } // Создание серверного сокета SOCKET master_sock; master_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (master_sock == INVALID_SOCKET) { fprintf(stderr, "socket() failed: %d\n", WSAGetLastError()); exit(1); } // Регистрация серверного сокета на порту оповещений CreateIoCompletionPort((HANDLE)master_sock, iocomp, 0, 0); // Привязка серверного сокета к заданному TCP-порту struct sockaddr_in master_addr; master_addr.sin_family = AF_INET; master_addr.sin_port = htons(portnum); master_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(master_sock, (struct sockaddr*)&master_addr, sizeof(master_addr))) { fprintf(stderr, "bind() failed: %d\n", WSAGetLastError()); exit(1); } // Перевод серверного сокета в режим прослушивания if (listen(master_sock, SOMAXCONN)) { fprintf(stderr, "listen() failed: %d\n", WSAGetLastError()); exit(1); } printf("[%d] Listening port %d for incoming connections...\n", master_sock, portnum); // Асинхронный прием запроса на подключение от первого клиента client_data *last_client = try_accept(master_sock); // Основной цикл сервера do { printf("waiting...\n"); u_long key; DWORD len; OVERLAPPED *pov; // Ожидание события на порту оповещений о завершении AIO if (!GetQueuedCompletionStatus(iocomp, &len, &key, &pov, INFINITE)) { fprintf(stderr, "[%d] completion failed: %d\n", key, GetLastError()); exit(1); } printf("[%d] completion %d bytes...\n", key ? ((client_data*)key)->sock : 0, len); // Обработка нового запроса на подключение if (key == 0) { // Опция, обеспечивающая сохраниение номеров TCP-портов: if (setsockopt(last_client->sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&master_sock, sizeof(master_sock))) { fprintf(stderr, "setsockopt failed: %d\n", WSAGetLastError()); exit(1); } struct sockaddr_in client_addr; int len1 = sizeof(client_addr); if (getpeername(last_client->sock, (struct sockaddr*)&client_addr, &len1)) { fprintf(stderr, "getpeername failed: %d\n", WSAGetLastError()); exit(1); } printf("[%d] Connected client: %s:%d\n", last_client->sock, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // Асинхронное чтение данных нового клиента try_read(last_client); // Асинхронный прием запроса на подключение от очередного клиента last_client = try_accept(master_sock); } else { // Обработка асинхронных данных клиента client_data *data = (client_data*)key; if (len == 0) { printf("[%d] disconnected...\n", data->sock); closesocket(data->sock); free(data); } else { if (send(data->sock, data->buf, len, 0) == SOCKET_ERROR) { fprintf(stderr, "send() failed: %d\n", GetLastError()); exit(1); } // Асинхронное чтение очередных данных try_read(data); } } } while (1); closesocket(master_sock); CloseHandle(iocomp); WSACleanup(); return 0; } /** * Асинхронный прием запроса на подключение очередного клиента * Создается клиентский сокет и необходимый контекст для операций ввода-вывода * @param master_sock Серверный сокет * @return Контекст клиента */ client_data* try_accept(SOCKET master_sock) { // Резервирование памяти под контекст клиента client_data *data = (client_data*) malloc(sizeof(client_data)); if (!data) { fprintf(stderr, "malloc failed()\n"); exit(1); } // Создание клиентского сокета data->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (data->sock == INVALID_SOCKET) { fprintf(stderr, "client socket() failed: %d\n", WSAGetLastError()); exit(1); } // Регистрация клиентского сокета на порту оповещений CreateIoCompletionPort((HANDLE)(data->sock), iocomp, (u_long)data, 0); //Запрос на асинхронный прием запроса memset(&(data->ov), 0, sizeof(OVERLAPPED)); if (!AcceptEx(master_sock, data->sock, data->buf, 0, ADDRLEN, ADDRLEN, &(data->len), &(data->ov))) { if (WSAGetLastError() == ERROR_IO_PENDING) { printf("[%d] acception in progress...\n", data->sock); } else { fprintf(stderr, "[%d] acceptex failed: %d\n", data->sock, WSAGetLastError()); exit(1); } } return data; } /** * Асинхронное чтение данных от клиента * @param data Контекст клиента */ void try_read(client_data *data) { memset(&(data->ov), 0, sizeof(OVERLAPPED)); if (!ReadFile((HANDLE)(data->sock), data->buf, BUFSIZE, NULL, &(data->ov))) { if (GetLastError() == ERROR_IO_PENDING) { printf("[%d] reading in progress...\n", data->sock); } else { fprintf(stderr, "[%d] read failed: %d\n", data->sock, GetLastError()); exit(1); } } }