ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.06.2019
Просмотров: 140
Скачиваний: 2
Лабораторная работа №5
Создание серверных приложений для асинхронного обмена данными на основе модели перекрытого вводы/вывода
Студента ИТ 14-1 Красовского Абхая
Цель работы: изучить возможности модели перекрытого вводы/вывода для создания серверных приложений работающих в асинхронном режиме.
Ход работы:
Сервер:
#pragma comment (lib,"ws2_32.lib") // Линковка библиотеки ws2_32.lib
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#include <list>
#include <conio.h>
#include <iostream>
using namespace std; // Использовать пространство имен std
#define BUFF_SIZE 1024 // Размер буфера
#define PORT 28912 // Номер порта
DWORD WINAPI ServerPool(HANDLE hp); //Функция потока сервера для обслуживания порта завершения
void SendToAll(char *buffer, unsigned long bytes); //Функция отсылки сообщения всем клиентам
SOCKET server_sock; // Прослушивающий сокет сервера
int ClientCount; // Счетчик клиентов
list<SOCKET> ClientList; // Список клиентов
//---------------------------------------------------------------------------
struct ovpConnection : public OVERLAPPED
{
int client_number; // Номер клиента
SOCKET c; // Сокет клиента
char * buffer; // Буфер сообщений
enum
{
op_type_send, // Посылка
op_type_recv // Прием
}op_type; // Тип операции
};
//---------------------------------------------------------------------------
void main()
{
system("color 1A");
setlocale(LC_ALL, "rus");
int err; // Возвращаемое значение
char buffer[128]; // Буфер для сообщений
WORD wVersionRequested; // Запрашиваемая версия
WSADATA wsaData; // Структура информации о сокетах
HANDLE hCp; // Описатель порта завершения
LPOVERLAPPED overlapped; // Структура асинхронного I/O
HANDLE hThread; // Дескриптор потока
DWORD ThreadId; // Идентификатор потока
DWORD flags; // Флаги функции WSARecv
wVersionRequested = MAKEWORD(2, 2); // Функция возвращающая DWORD из промежутка чисел
err = WSAStartup(wVersionRequested, &wsaData); //Инициализация библиотеки ws2_32.dll
if (err == SOCKET_ERROR)
{
strcpy(buffer, "Ошибка функции WSAStartup");
CharToOem((LPCWSTR)buffer, buffer); // Преобразования строки в ANSI кодировку
printf("%s %d\n", buffer, WSAGetLastError());
WSACleanup(); // Завершение работы
getch();
return;
}
//Создаем порт завершения
hCp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // FileHandle, ExistingCompletionPort, CompletionKey, NumberOfConcurrentThreads
if (hCp == NULL)
{
strcpy(buffer, "Ошибка функции CreateIoCompletionPort");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, GetLastError());
WSACleanup(); // Завершение работы
getch();
return;
}
// Задаем параметры для прослушивающего сокета сервера
server_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED); // lpProtocolInfo, GROUP, dwFlags
if (server_sock == INVALID_SOCKET)
{
strcpy(buffer, "Ошибка функции WSASocket");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, WSAGetLastError());
WSACleanup(); //Завершение работы с сокетами
getch();
return;
}
else
{
// Используем ранее созданный порт завершения
if (CreateIoCompletionPort((HANDLE)server_sock, hCp, 0, 0) == NULL)
{
strcpy(buffer, "Ошибка функции CreateIoCompletionPort");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, GetLastError());
WSACleanup(); //Завершение работы
getch();
return;
}
}
//Заполняем структуру адреса и подключаем сокет к коммуникационной среде
SOCKADDR_IN sinServer;
sinServer.sin_family = AF_INET;
sinServer.sin_port = htons(PORT);
sinServer.sin_addr.s_addr = INADDR_ANY;
err = bind(server_sock, (LPSOCKADDR)&sinServer, sizeof(sinServer));
if (err == -1)
{
strcpy(buffer, "Ошибка функции bind");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, GetLastError());
WSACleanup(); //Завершение работы
getch();
return;
}
//Создаем очередь для ожидания запросов от клиентов на соединение
err = listen(server_sock, SOMAXCONN);
if (err == -1)
{
strcpy(buffer, "Ошибка функции listen №");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, GetLastError());
WSACleanup(); // Завершение работы
getch();
return;
}
//Cоздаем рабочий поток для обслуживания сообщений от порта завершения
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ServerPool, hCp, 0, &ThreadId);
ClientCount = 0;
strcpy(buffer, "Сервер запущен\n");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s", buffer);
//Бесконечный цикл для многократного обслушивания запросов от клиентов
while (true)
{
//Принимаем запрос от программы-клиента на установление связи
SOCKADDR_IN sinClient;
int lenClient = sizeof(sinClient);
SOCKET client = accept(server_sock, (struct sockaddr*)&sinClient, &lenClient);
CreateIoCompletionPort((HANDLE)client, hCp, 0, 0);
//Добавляем клиента в список
ClientList.insert(ClientList.end(), client);
// Создаем overlapped-структуру
ovpConnection *op = new ovpConnection;
//Заполняем overlapped-структуру
op->c = client;
op->op_type = ovpConnection::op_type_recv;
op->buffer = new char[BUFF_SIZE];
op->hEvent = 0;
op->client_number = ++ClientCount;
strcpy(buffer, "Клиент № %d подключился, активных клиентов %d\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, ClientCount, ClientList.size());
unsigned long b;
WSABUF buf;
buf.buf = op->buffer;
buf.len = BUFF_SIZE;
flags = 0;
err = WSARecv(op->c, &buf, 1, &b, &flags, op, 0); // socket, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine(Указатель на подпрограмму завершения вызывается, когда операция приема была завершена)
if (!err)
{
strcpy(buffer, "Ошибка функции WSARecv");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, WSAGetLastError());
}
}
return;
}
//---------------------------------------------------------------------------
//Функция потока сервера для обслуживания порта завершения
//---------------------------------------------------------------------------
DWORD WINAPI ServerPool(HANDLE hp)
{
int err; // Возвращяемое значение
unsigned long bytes; // Количество байтов
unsigned long key; // Значение, асоциированное с дескриптором порта
char buffer[128]; // Буфер для сообщений
LPOVERLAPPED overlapped; // Структура асинхронного I/O
HANDLE hport = hp; // Дескриптор порта
DWORD flags; // Флаги функции WSARecv()
while (true)
{
// Получаем информацию о завершении операции
if (GetQueuedCompletionStatus(hport, &bytes, &key, &overlapped, INFINITE))
{
// Операция завершена успешно
ovpConnection * op = (ovpConnection*)overlapped;
// Определяем тип завершенной операции и выполняем соответствующие действия
switch (op->op_type)
{
//Завершена отправка данных
case ovpConnection::op_type_send:
delete[] op->buffer;
delete op;
break;
//Завершен приём данных
case ovpConnection::op_type_recv:
if (!bytes)
{
//Соединение с данным клиентом закрыто
ClientList.remove(op->c);
closesocket(op->c);
strcpy(buffer, "Клиент № %d отключился, активных клиентов %d\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, op->client_number, ClientList.size());
break;
}
op->buffer[bytes] = '\0';
if (op->buffer[0] == '*') //Звездочка * - признак приема сообщения, которое
//должно быть переслано всем подключенным клиентам
{
strcpy(buffer, "От клиента № %d получено сообщение для всех %s\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, op->client_number, (op->buffer + 1));
SendToAll(op->buffer, bytes); //Отправляем данные всем
}
else
{
strcpy(buffer, "От клиента № %d получено сообщение %s\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, op->client_number, op->buffer);
}
unsigned long b;
WSABUF buf;
buf.buf = op->buffer;
buf.len = BUFF_SIZE; // buffer_len – постоянная величина
err = WSARecv(op->c, &buf, 1, &b, &flags, op, 0);
if (!err)
{
strcpy(buffer, "Ошибка функции WSARecv");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, WSAGetLastError());
}
}
}
else
{
if (!overlapped)
{
// Ошибка с портом
// Закрываем все сокеты, закрываем порт, очищаем список
for (list<SOCKET>::iterator i = ClientList.begin(); i != ClientList.end(); i++)
{
closesocket(*i);
}
ClientList.clear();
closesocket(server_sock);
CloseHandle(hport);
strcpy(buffer, "Ошибка порта № %d, сервер завершает работу\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, GetLastError());
getch();
exit(0); // Функция выхода из потока
}
else
{
//Закрываем соединение с клиентом
closesocket(((ovpConnection*)overlapped)->c);
ClientList.remove(((ovpConnection*)overlapped)->c);
strcpy(buffer, "Клиент № %d отключился, активных клиентов %d\n");
CharToOem((LPCWSTR)buffer, buffer);
printf(buffer, ((ovpConnection*)overlapped)->client_number, ClientList.size());
}
}
}
return 0;
}
//---------------------------------------------------------------------------
//Функция отсылки данных всем клиентам
//---------------------------------------------------------------------------
void SendToAll(char *buffer, unsigned long bytes)
{
//Перебираем все соединения
for (list<SOCKET>::iterator i = ClientList.begin(); i != ClientList.end(); i++)
{
ovpConnection * op = new ovpConnection;
op->c = *i;
op->op_type = ovpConnection::op_type_send;
op->buffer = new char[bytes - 1];
memcpy(op->buffer, (buffer + 1), bytes - 1);
op->buffer[bytes - 1] = '\0';
unsigned long b;
WSABUF buf;
buf.buf = op->buffer;
buf.len = BUFF_SIZE;
WSASend(op->c, &buf, 1, &b, 0, op, 0);
}
return;
}
Клиент:
#pragma comment (lib,"ws2_32.lib")
#include <stdio.h>
#include <winsock2.h>
#include <iostream>
#define PORT 28912 // Номер порта сервера
DWORD WINAPI ClientPool(SOCKET client); // Функция потока клиента
void main()
{
system("color 1A");
setlocale(LC_ALL, "rus");
int j; // Счетчик
WORD wVersionRequested; // Запрашиваемая версия
WSADATA wsaData; // Структура информации о сокетах
int err; // Возвращяемое значение
char address[16] = { 0 }; // Адрес удаленного компьютера(сервера)
char buffer[128]; // Буфер для сообщений
SOCKET sd; // Сокет клиента
HANDLE hThread; // Дескриптор потока
DWORD ThreadId; // Идентификатор потока
//Инициализируем процесс библиотеки ws2_32.dll
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err == SOCKET_ERROR) {
strcpy(buffer, "Ошибка функции WSAStartup № ");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, WSAGetLastError());
WSACleanup(); // Завершение работы при неудаче
getch();
return;
}
strcpy(buffer, "Введите адрес удаленного компьютера");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s\n", buffer);
scanf("%s", address);
// Cоздаем сокет
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == INVALID_SOCKET) {
strcpy(buffer, "Ошибка функции socket ");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, WSAGetLastError());
WSACleanup(); // Завершение работы
getch();
return;
}
SOCKADDR_IN sin;//Структура для размещения адреса сервера
ZeroMemory(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(address);
if (connect(sd, (PSOCKADDR)&sin, sizeof(SOCKADDR)) == -1)
{
strcpy(buffer, "Не могу подключиться к серверу №");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s %d\n", buffer, GetLastError());
WSACleanup(); // Завершение работы
getch();
return;
}
strcpy(buffer, "Подключение к серверу успешно");
CharToOem((LPCWSTR)buffer, buffer);
printf("%s\n", buffer);
// Создаем поток для исполнения функции ClientPool()
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ClientPool, (void*)sd, 0, &ThreadId);
while (true)
{
scanf("%s", buffer);
if (strcmp(buffer, "EXIT") == 0)
{
TerminateThread(hThread, 0); // асинхнронная функция "убивания" потока
if (sd != INVALID_SOCKET)closesocket(sd); // Закрываем сокет
break;
}
send(sd, buffer, strlen(buffer), 0);
}
WSACleanup(); // Завершение работы
return;
}
//---------------------------------------------------------------------------
// Функция потока клиента
//---------------------------------------------------------------------------
DWORD WINAPI ClientPool(SOCKET client)
{
int bytes;
char buffer[128];
while (true)
{
bytes = recv(client, buffer, sizeof(buffer), 0);
buffer[strlen(buffer)] = '\0';
if (strlen(buffer) != 0)printf("%s\n", buffer);
}
return 0;
}