ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 12.06.2019

Просмотров: 126

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Лабораторная работа №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 <conio.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;

}



Смотрите также файлы