

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);
}
unsigned
long 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());
}
}
}
return
0;
}
//---------------------------------------------------------------------------
//Функция отсылки данных всем клиентам //---------------------------------------------------------------------------
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;
}