Я использую схему реактора POCO для обработки входящих соединений TCP. Соединения могут занять от нескольких секунд до минут в зависимости от типа запроса следующим образом:
try{
ServerSocket serverSocket(port);
reactor = new SocketReactor();
ParallelSocketAcceptor<BFSTcpServiceHandler,SocketReactor> acceptor(serverSocket, *reactor);
//Start Reactor
reactor->run();
}catch(Exception&e){
LOG(ERROR)<<"ERROR in initializing TCPServer:"<<e.message();
return;
}
А вот и обработчик:
BFSTcpServiceHandler::BFSTcpServiceHandler(StreamSocket& _socket,
SocketReactor& _reactor): socket(_socket),reactor(_reactor) {
//Set Keeep Alive for socket
socket.setKeepAlive(false);
//Register Callbacks
reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
ReadableNotification>(*this, &BFSTcpServiceHandler::onReadable));
/*reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
WritableNotification>(*this, &BFSTcpServiceHandler::onWriteable));*/
reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
ShutdownNotification>(*this, &BFSTcpServiceHandler::onShutdown));
reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
ErrorNotification>(*this, &BFSTcpServiceHandler::onError));
reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
TimeoutNotification>(*this, &BFSTcpServiceHandler::onTimeout));
/*reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
IdleNotification>(*this, &BFSTcpServiceHandler::onIdle));*/
}
BFSTcpServiceHandler::~BFSTcpServiceHandler() {
//Unregister Callbacks
reactor.removeEventHandler(socket, NObserver<BFSTcpServiceHandler,
ReadableNotification>(*this, &BFSTcpServiceHandler::onReadable));
...
//Close socket
try {
socket.close();
}catch(...){}
}
void BFSTcpServiceHandler::onReadable(
const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
//LOG(ERROR)<<"onReadable:"<<socket.peerAddress().toString();
try{
//Read and process request
} catch(Exception &e){
LOG(ERROR)<<"Error in reading request:"<<e.message();
delete this;
}
//So after a connection is served just close it!
delete this;
}
void BFSTcpServiceHandler::onShutdown(
const Poco::AutoPtr<Poco::Net::ShutdownNotification>& pNf) {
LOG(ERROR)<<"onShutdown:"<<socket.peerAddress().toString();
//Call destructor of this class
delete this;
}
void BFSTcpServiceHandler::onWriteable(
const Poco::AutoPtr<Poco::Net::WritableNotification>& pNf) {
static bool once = true;
if(once) {
LOG(ERROR)<<"onWritable:"<<socket.peerAddress().toString()<<" keepAlive?"<<socket.getKeepAlive()<<" isBlocking?"<<socket.getBlocking()<<" noDeley?"<<socket.getNoDelay();
once = false;
}
}
void BFSTcpServiceHandler::onTimeout(
const Poco::AutoPtr<Poco::Net::TimeoutNotification>& pNf) {
LOG(ERROR)<<"\nTIMEOUT! onTimeout:"<<socket.peerAddress().toString();
}
void BFSTcpServiceHandler::onError(
const Poco::AutoPtr<Poco::Net::ErrorNotification>& pNf) {
LOG(ERROR)<<"\nERROR! onError:"<<socket.peerAddress().toString();
}
void BFSTcpServiceHandler::onIdle(
const Poco::AutoPtr<Poco::Net::IdleNotification>& pNf) {
LOG(ERROR)<<"\nIDLE! onIdle:"<<socket.peerAddress().toString();
}
Код работает нормально; однако через некоторое время он застревает, что означает, что сервер принимает соединения, но onReadable больше не вызывается. Например, после зависания я могу подключиться к серверу через telnet, но при отправке данных onReadable не запускается. Используя netstat, я понял, что некоторые данные хранятся в RCV_QEUEUE, и реактор не запускает событие onReadable.
Я думал, что это происходит из-за того, что системы нарушают лимиты соединений / файлов, но на самом деле не так много открытых соединений, когда система застревает.
Любой комментарий или помощь приветствуется.
Спасибо,
Проблема заключалась в использовании неисправного NIC / драйвера. Я изменил код на обычные POSIX-сокеты, и у меня возникла та же проблема, и переключение сетевой карты решило проблему. Я не уверен, была ли это проблема с драйверами или оборудованием.