Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
39
Добавлен:
13.02.2015
Размер:
129.14 Кб
Скачать

switch (PerIoData->OperationType)

{

case SRV_NEW_CONNECTION:

//это первое подключение:

...

case SRV_DATA_SEND:

//посылаем данные

...

case SRV_DATA_RECV:

//принимаем данные

...

case SRV_DISCONNECT:

//отсоединение

.......

}

При коректном завершении работы порта завершения главное - не освобождать память со структурой OVERLAPPED, пока выполняется какая-нибудь операция на сокете. Так же надо знать, что закрытие сокета с помощью closesocket() прерывает любые продолжающиеся операции на данном порту завершения. После того как вы закроете все сокеты, необходимо завершить все рабочие потоки порта завершения. Для этого воспользуемся функцией PostQueuedCompletionStatus(), которая отправит потоку пакет, заставляющий прекратить работу:

BOOL PostQueuedCompletionStatus(

 

HANDLE CompletionPort,

// дескриптор порта завершения

DWORD dwNumberOfBytesTransferred,

// возврат из

GetQueuedCompletionStatus()

 

DWORD dwCompletionKey,

// возврат из

GetQueuedCompletionStatus()

 

LPOVERLAPPED lpOverlapped

// возврат из

GetQueuedCompletionStatus()

 

);

 

Параметр CompletionPort - задаёт порт завершения, а остальные параметры задают значения, которые поток получит из функции GetQueuedCompletionStatus() - те мы можем задать непосредственно тип операции для завершения работы - когда поток получит это значение и интерпретирует его соответвующим образом мы можем освободить какие-то ресурсы, сделать какую-то работу, Etc...

Рабочий поток получает эти три параметра GetQueuedCompletionStatus() и он может определить, когда ему необходимо завершить свою работу с помощью специального значения, переданного в одном из этих параметров. Например, можно передавать значение 0 в dwCompletionKey-параметре, который рабочий поток будет интерпретировать как команда завершения работы.

После закрытия всех рабочих потоков надо закрыть порт завершения через CloseHandle() и завершить программу.

Прoграммы для работы с портом завершения IOCP

Программа-клиент с использованием потока

#include<stdio.h> #include<conio.h> #include<malloc.h> #include<winsock2.h>

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

#define PORT 28912// Номер порта сервера

DWORD WINAPI ClientPool(SOCKET client); // Функция потока клиента //---------------------------------------------------------------------------

void

main

(

void

)

{

 

 

 

 

 

 

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

(

buffer,buffer

)

;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

printf

(

"%s %d\n"

,buffer, WSAGetLastError

())

;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

WSACleanup

()

;

// Завершение работы при неудаче

 

 

 

 

 

 

 

 

 

getch

()

;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

return

;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

strcpy

(

buffer,

"Введите адрес удаленного компьютера"

)

;

 

 

 

 

CharToOem

(

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

(

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(buffer,buffer);

printf("%s %d\n",buffer, GetLastError());

WSACleanup(); // Завершение работы

getch();

return;

}

strcpy(buffer,"Подключение к серверу успешно");

CharToOem(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);

}

return0;

}

Программа-сервер с использованием модели IOCP

#include

<

stdio.h

>

 

 

 

 

 

 

 

 

 

 

#include

<

conio.h

>

 

 

 

 

 

 

 

 

 

 

#include

<

malloc.h

 

>

 

 

 

 

 

 

 

 

 

#include

<

Winsock2.h

 

>

 

 

 

 

 

 

 

 

#include

<

list

>

 

 

 

// Используем STL

 

 

 

#pragma

 

comment

(

lib,

"ws2_32.lib"

)

// Линковка библиотеки ws2_32.lib

 

using

namespace

 

std;

 

 

 

 

// Использовать пространство имен

std

#define

BUFF_SIZE

1024

// Размер буфера

 

#define

PORT 28912

 

// Номер порта

 

DWORD WINAPI ServerPool(HANDLE hp);

void SendToAll(char *buffer,unsignedlong 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

(

int

argc,

char

*argv

[])

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

int

err;

// Возвращаемое значение

 

 

 

char

buffer

[

128

]

;

// Буфер для сообщений

 

 

 

 

 

WORD

wVersionRequested;

// Запрашиваемая версия

 

 

 

 

WSADATA wsaData;

// Структура инф-ции о

сокетах

 

HANDLE hCp;

// Описатель порта завершения

 

 

LPOVERLAPPED overlapped;

// Структура асинхронного I/O

HANDLE hThread;

// Хендл потока

 

 

 

DWORD

ThreadId;

//

Идентификатор потока

 

DWORD

flags;

//

Флаги ф-ции WSARecv

 

//Инициализация библиотеки ws2_32.dll wVersionRequested=MAKEWORD(2,2); err=WSAStartup(wVersionRequested,&wsaData); if(err==SOCKET_ERROR)

{

strcpy(buffer,"Ошибка ф-ции WSAStartup");

CharToOem(buffer,buffer);

printf("%s %d\n",buffer, WSAGetLastError());

WSACleanup(); // Завершение работы

getch();

return;

}

//Создаем порт завершения hCp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); if(hCp==NULL)

{

strcpy(buffer,"Ошибка ф-ции CreateIoCompletionPort");

CharToOem(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) if(server_sock==INVALID_SOCKET)

{

strcpy(buffer,"Ошибка ф-ции WSASocket");

CharToOem(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(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(buffer,buffer);

printf("%s %d\n",buffer, GetLastError());

WSACleanup(); //Завершение работы

getch();

return;

}

//Создаем очередь для ожидания запросов от клиентов на соединение err = listen(server_sock, SOMAXCONN);

if(err==-1)

{

strcpy(buffer,"Ошибка ф-ции listen №");

CharToOem(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(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->sock_handle = client;

op->op_type = ovpConnection::op_type_recv; op->buffer = newchar[BUFF_SIZE]; op->hEvent = 0; op->client_number=++ClientCount;

strcpy(buffer,"Клиент № %d подключился, активных клиентов %d\n"); CharToOem(buffer,buffer); printf(buffer,ClientCount,ClientList.size());

unsignedlong b; WSABUF buf;

buf.buf = op->buffer; buf.len = BUFF_SIZE; flags=0;

err=WSARecv(op->sock_handle, &buf, 1, &b, &flags, op, 0); if(!err)

{

strcpy(buffer,"Ошибка ф-ции WSARecv");

CharToOem(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->sock_handle); closesocket(op->sock_handle);

strcpy(buffer,"Клиент № %d отключился, активных клиентов %d\n");

CharToOem(buffer,buffer);

printf(buffer,op->client_number,ClientList.size());

break;

}

op->buffer[bytes]='\0';

if(op->buffer[0]=='*')//Звездочка * - признак приема сообщения, которое //должно быть переслано всем подключенным клиентам

{

strcpy(buffer,"От клиента № %d получено сообщение для всех %s\n");

CharToOem(buffer,buffer);

printf(buffer,op->client_number,(op->buffer+1));

SendToAll(op->buffer, bytes); //Отправляем данные всем

}

else

{

strcpy(buffer,"От клиента № %d получено сообщение %s\n");

CharToOem(buffer,buffer);

printf(buffer,op->client_number,op->buffer);

}

unsignedlong b;

WSABUF buf;

buf.buf = op->buffer;

buf.len = BUFF_SIZE; // buffer_len – постоянная величина

err=WSARecv(op->sock_handle, &buf, 1, &b, &flags, op, 0);

if(!err)

{

strcpy(buffer,"Ошибка ф-ции WSARecv");

CharToOem(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(buffer,buffer);

printf(buffer,GetLastError()); getch();

exit(0);

}

else

{

//Закрываем соединение с клиентом

closesocket(((ovpConnection*)overlapped)->sock_handle);

ClientList.remove(((ovpConnection*)overlapped)->sock_handle);

strcpy(buffer,"Клиент № %d отключился, активных клиентов %d\n");

CharToOem(buffer,buffer);

printf(buffer,((ovpConnection*)overlapped)->client_number,

ClientList.size());

}

}

}

return0;

}

//---------------------------------------------------------------------------

//Функция отсылки данных всем клиентам //---------------------------------------------------------------------------

void SendToAll(char *buffer,unsignedlong bytes)

{

//Перебираем все соединения

for(list<SOCKET>::iterator i=ClientList.begin();i!=ClientList.end();i++)

{

ovpConnection * op = new ovpConnection; op->sock_handle = *i;

op->op_type = ovpConnection::op_type_send; op->buffer = newchar[bytes-1]; memcpy(op->buffer, (buffer+1), bytes-1); op->buffer[bytes-1]='\0';

unsignedlong b; WSABUF buf;

buf.buf = op->buffer;

buf.len = BUFF_SIZE; WSASend(op->sock_handle,&buf,1,&b, 0, op, 0);

}

return;

}

Соседние файлы в папке Программирование_распределенных_систем