Модель порта завершения (комментарии)
Это сообщение сгенерировано автоматически.
Очень хорошая статья, понравилась.
Есть пару моментов которые нужно уточнить:
1. Сколько я не пытался мне не удалось отправить серверу более 1 сообщения от одного клиента, это так и задумано или вкралась ошибка ?
2. В главном цикле происходит прерывание работы сервера до подключения нового клиента или до получении данных от клиентов (в том цикле где ожидаются коннекты от клиента), т.е. получается, чтобы мой сервер помимо обработки запросов клиентов выполнял другие действия наприме общет ИИ, НПС и прочего необходимо их делать в отдельных потоках или создавать отдельные сервера, это нельзя обойти используя модель порта завершения?
3. Есть небольшая опечатка в структуре
struct ovpConnection: public OVERLAPPED
{
int client_number; // Номер клиента
SOCKET с; // Сокет клиента (((переменная должна называться sock_handle))
char * buffer; // Буфер сообщений
enum
{
op_type_send, // Посылка
op_type_recv // Прием
}op_type; // Тип операции
};
Scorp_progr
>Очень хорошая статья, понравилась.
>Есть пару моментов которые нужно уточнить:
>1. Сколько я не пытался мне не удалось отправить серверу более 1 сообщения от
>одного клиента, это так и задумано или вкралась ошибка ?
ммм, вообщето нет. ошибка либо в клиенте, либо сервер после получения первого пакета не переводится в ожидание приема второго.
есть только одна засада, с которой я бился пару дней пока случайно не догадался, у меня пакет устроен так:
1. 4 байта это размер тела пакета
2. тело пакета.
соотвественно на сервере строго сначала читаю 4 байта, а потом читал столько, сколько было прислано клиентом. и все, после этого сервак переставал принимать пакеты.
надо после приема пакета полностью снава "заказывать" чтение 4х байт заголовка пакета с размером пакета и уходить т.к скорее всего операция чтения вернет ошибку WSA_IO_PENDING, зато потом когда эти данные на сервер прийдут - он сработает в штатном режиме, вызвав соотвествующий обработчик
>2. В главном цикле происходит прерывание работы сервера до подключения нового
>клиента или до получении данных от клиентов (в том цикле где ожидаются коннекты
>от клиента),
да, фактически там ждется совершение события - получение дланных. подключение клиента это в другом потоке происходит.
>т.е. получается, чтобы мой сервер помимо обработки запросов
>клиентов выполнял другие действия наприме общет ИИ, НПС и прочего необходимо их
>делать в отдельных потоках или создавать отдельные сервера, это нельзя обойти
>используя модель порта завершения?
для обсчета игровых моментов надо делать отдельный поток.
>3. Есть небольшая опечатка в структуре
>struct ovpConnection: public OVERLAPPED
>{
> int client_number; // Номер клиента
> SOCKET с; // Сокет клиента (((переменная должна называться
>sock_handle))
> char * buffer; // Буфер сообщений
> enum
> {
> op_type_send, // Посылка
> op_type_recv // Прием
> }op_type; // Тип операции
>};
копипаст с моего сервера
struct ovpConnection: public OVERLAPPED { ovpConnection(); int client_number; // Номер клиента IClient* m_pClient; // интерфейс клиента SOCKET sock_handle; // Сокет клиента char * buffer; // Буфер сообщений std::string xml_string; // Буфер xml сообщений IBody* packet; enum { SRV_NEW_CONNECTION, // это первое подключение: SRV_DATA_SEND, // посылаем данные SRV_DATA_RECV, // принимаем данные SRV_DISCONNECT }OperationType; // Тип операции };
С повторной отправкой сообщения от клиента серверу я разобрался.
Теперь другая проблема, отправка сообщения от сервера клиенту не проходит, если взять пример сервера и клиента из статьи и его скомпилить. Сервер должен отсылать всем подключившимся клиентам сообщение если оно имеет вид *текст, сервер не отсылает у меня такое ощущение что проблемы в функции SendAll, вот ее текст:
void SendToAll(char *buffer,unsigned long 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 = 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->sock_handle,&buf,1,&b, 0, op, 0);
}
return;
}
В чем особенность функции WSASend ?
void CCompletitionPort::SendTo(SOCKET sock_handle, char *buffer, unsigned long bytes ) { ovpConnection * op = new ovpConnection; op->sock_handle = sock_handle; op->OperationType = ovpConnection::SRV_DATA_SEND; op->buffer = new char[bytes+1]; memcpy( op->buffer, buffer, bytes ); unsigned long b; WSABUF buf; buf.buf = op->buffer; buf.len = bytes; WSASend( op->sock_handle, &buf, 1, &b, 0, op, 0); return; } void CCompletitionPort::SendToAll( char *buffer, unsigned long bytes) { //Перебираем все соединения for( list<SOCKET>::iterator i=ClientList.begin( ); i!=ClientList.end( ); i++) CCompletitionPort::SendTo( *i, buffer, bytes ); return; }
а в главном цикле
//--------------------------------------------------------------------------- //Функция потока сервера для обслуживания порта завершения //--------------------------------------------------------------------------- DWORD WINAPI CCompletitionPort::ServerPool(LPVOID lpvParam ) { ....... case ovpConnection::SRV_DATA_SEND://Завершена отправка данных delete [] op->buffer; delete op; break; .....
про WSASend см MSDN
В функции WSASend есть неточность из-за которой она не работает, обязательно нужно добавить строчку выделенную жирным
void SendToAll(char *buffer,unsigned long 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 = new char[bytes-1];
memcpy(op->buffer, (buffer+1), bytes-1);
op->buffer[bytes-1]='\0';
op->hEvent = 0;
unsigned long b;
WSABUF buf;
buf.buf = op->buffer;
buf.len = BUFF_SIZE;
WSASend(op->sock_handle,&buf,1,&b, 0, op, 0);
}
return;
}
Scorp_progr
>В функции WSASend есть неточность из-за которой она не работает, обязательно
>нужно добавить строчку выделенную жирным
>void SendToAll(char *buffer,unsigned long 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 = new char[bytes-1];
>memcpy(op->buffer, (buffer+1), bytes-1);
>op->buffer[bytes-1]='\0';
>op->hEvent = 0;
>unsigned long b;
>WSABUF buf;
>buf.buf = op->buffer;
>buf.len = BUFF_SIZE;
>WSASend(op->sock_handle,&buf,1,&b, 0, op, 0);
>}
> return;
>}
для этого есть конструктор
CCompletitionPort::ovpConnection::ovpConnection() { hEvent = 0; packet = NULL; m_pClient = NULL; xml_string = ""; }
у меня вот такой вопрос:
если мы на стороне сервера изнутри нашего порта завершения хотим закрыть какой-то сокет, то мы вызываем closesocket().
если у нас были незавершенные асинхронные операции с этим сокетом, то они должны прерваться.
в этом случае GetQueuedCompletionStatus вернет нам типа FALSE. это хорошо. мы можем в этот момент удалить из памяти объекты типа ovpConnection.
если у нас было несколько незавершенных асинхронных операций, то для каждой из них сработает GetQueuedCompletionStatus.
а как быть с объектом, который связан непосредственно с сокетом _PER_HANDLE_DATA ?
в какой момент удалять из памяти его? как мы узнаем, что уже можно?
Возникло таких 2 вопроса:
- в SendTo() создается экземпляр ovpConnection * op = new ovpConnection;
Когда/где для него освобождается память?...
- если выполнить несколько SendTo() для одного сокета с разными буферами разных размеров, дойдут ли они до клиента в порядке отправки, или порядок может измениться, так как посылаются данные асинхронно? Если да, то как решается такая проблема при использовании нескольких workerthread?
Ничего удобовразумительного нагуглить не удалось... :(
Тема в архиве.