Иногда повышение:: asio, кажется, разъединяется, прежде чем я захочу его к, т.е. прежде чем сервер правильно обработает разъединение. Я не уверен, как это возможно, потому что клиент, кажется, думает его полностью отправленный сообщение, все же когда сервер не испускает ошибку его даже чтение заголовок сообщения... Во время тестирования это только происходит, возможно, каждый 5-й раз, сервер получает клиентское сообщение закрытия и разъединяет клиент чисто.
Ошибка: "Существующее соединение было насильственно закрыто удаленным хостом"
Клиент, разъединяющийся:
void disconnect()
{
boost::system::error_code error;
//just creates a simple buffer with a shutdown header
boost::uint8_t *packet = createPacket(PC_SHUTDOWN,0);
//sends it
if(!sendBlocking(socket,packet,&error))
{
//didnt get here in my tests, so its not that the write failed...
logWrite(LOG_ERROR,"server",
std::string("Error sending shutdown message.\n")
+ boost::system::system_error(error).what());
}
//actaully disconnect
socket.close();
ioService.stop();
}
bool sendBlocking(boost::asio::ip::tcp::socket &socket,
boost::uint8_t *data, boost::system::error_code* error)
{
//get the length section from the message
boost::uint16_t len = *(boost::uint16_t*)(data - 3);
//send it
asio::write(socket, asio::buffer(data-3,len+3),
asio::transfer_all(), *error);
deletePacket(data);
return !(*error);
}
Сервер:
void Client::clientShutdown()
{
//not getting here in problem cases
disconnect();
}
void Client::packetHandler(boost::uint8_t type, boost::uint8_t *data,
boost::uint16_t len, const boost::system::error_code& error)
{
if(error)
{
//error handled here
delete[] data;
std::stringstream ss;
ss << "Error recieving packet.\n";
ss << logInfo() << "\n";
ss << "Error: " << boost::system::system_error(error).what();
logWrite(LOG_ERROR,"Client",ss.str());
disconnect();
}
else
{
//call handlers based on type, most will then call startRead when
//done to get the next packet. Note however, that clientShutdown
//does not
...
}
}
void startRead(boost::asio::ip::tcp::socket &socket, PacketHandler handler)
{
boost::uint8_t *header = new boost::uint8_t[3];
boost::asio::async_read(socket,boost::asio::buffer(header,3),
boost::bind(&handleReadHeader,&socket,handler,header,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket, PacketHandler handler,
boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
if(error)
{
//error "thrown" here, len always = 0 in problem cases...
delete[] header;
handler(0,0,0,error);
}
else
{
assert(len == 3);
boost::uint16_t payLoadLen = *((boost::uint16_t*)(header + 0));
boost::uint8_t type = *((boost::uint8_t*) (header + 2));
delete[] header;
boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];
boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
boost::bind(&handleReadBody,socket,handler,
type,payLoad,payLoadLen,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
}
void handleReadBody(ip::tcp::socket *socket, PacketHandler handler,
boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
size_t readLen, const boost::system::error_code& error)
{
if(error)
{
delete[] payLoad;
handler(0,0,0,error);
}
else
{
assert(len == readLen);
handler(type,payLoad,len,error);
//delete[] payLoad;
}
}
Я думаю, вам, вероятно, следует позвонить в сокет . shutdown (boost :: asio :: ip :: tcp :: socket :: shutdown_both, ec)
перед вызовом socket.close ()
.
В документации boost :: asio для basic_stream_socket :: close указано:
Для переносимого поведения в отношении постепенного закрытия подключенного сокета вызовите shutdown () перед закрытием сокета.
Это должно гарантировать, что все ожидающие операции на сокете должным образом отменены и все буферы очищаются до вызова socket.close.
Может быть, вот что происходит:
Я вижу в ваших обработчиках чтения, что если есть ошибка, вы никогда не проверяете, есть ли ваш завершающий пакет. Может быть и так. В основном, я хочу сказать, что, возможно, ваш клиент иногда может послать и закрытый, и завершающий пакет до того, как у сервера появится возможность обработать их по отдельности.
Используйте async_write() и поместите socket.close() внутрь обработчика записи. Это гарантирует, что пакет обрабатывается boost asio и не игнорируется в середине обработки (из-за вызовов close()).
У меня очень похожая проблема. Я считаю, что это связано с повторным использованием соединений Windows.Следующее знакомо?
Спецификации tcp указывают, что по умолчанию он должен ждать четыре минуты для окончательного подтверждения при закрытии tcp-соединения. Вы можете увидеть эти соединения в состоянии FIN_WAIT, используя netstat. ОС Windows обнаруживает, когда вы пытаетесь подключиться к той же самой системе, берет эти частично закрытые соединения и перерабатывает их. Ваш второй вызов программы получает «закрытое» соединение, оставленное первым запуском. Он получает следующее подтверждение, а затем действительно закрывается.