Libevent великолепен, и я люблю его до сих пор. Однако на эхо-сервере запись только отправляется в сокет при второй записи. Я пишу из другого потока, потока насоса, который общается с БД и выполняет минимальное массирование данных.
Я подтвердил это, настроив обратный вызов для записи:
bufferevent_setcb( GetBufferEvent(), DataAvailable, DataWritten, HandleSocketError, this );
вызов bufferevent_flush (m_bufferEvent, EV_READ | EV_WRITE, BEV_NORMAL), похоже, не имеет никакого эффекта.
Вот настройка, на случай, если я ее где-нибудь взорвал. Я значительно упростил накладные расходы в моей кодовой базе, чтобы получить некоторую помощь. Это включает в себя инициализацию сокетов, инициализацию моего потока и т. Д. Это многопоточное приложение, поэтому там могут быть некоторые проблемы. Я начну с этого:
m_LibEventInstance = event_base_new();
evthread_use_windows_threads();
m_listener = evconnlistener_new_bind( m_LibEventInstance,
OnAccept,
this,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_CLOSE_ON_EXEC | LEV_OPT_REUSEABLE,
-1,// no maximum number of backlog connections
(struct sockaddr*)&ListenAddress, socketSize );
if (!m_listener) {
perror("Couldn't create listener");
return false;
}
evconnlistener_set_error_cb( m_listener, OnSystemError );
AFAIK, это копирование и вставка из образцов, поэтому оно должно работать. Мой OnAccept делает следующее:
void OnAccept( evconnlistener* listenerObj, evutil_socket_t newConnectionId, sockaddr* ClientAddr, int socklen, void* context )
{
// We got a new connection! Set up a bufferevent for it.
struct event_base* base = evconnlistener_get_base( listenerObj );
struct bufferevent* bufferEvent = bufferevent_socket_new( base, newConnectionId, BEV_OPT_CLOSE_ON_FREE );
bufferevent_setcb( GetBufferEvent(), DataAvailable, DataWritten,
HandleSocketError, this );
// We have to enable it before our callbacks will be called.
bufferevent_enable( GetBufferEvent(), EV_READ | EV_WRITE );
DisableNagle( m_connectionId );
}
Теперь я просто отвечаю на поступающие данные и сохраняю их в буфере для последующей обработки. Это многопоточное приложение, поэтому я буду обрабатывать данные позже, обрабатывать их или возвращать ответ клиенту.
void DataAvailable( struct bufferevent* bufferEventObj, void* arg )
{
const U32 MaxBufferSize = 8192;
MyObj* This = (MyObj*) arg;
U8 data[ MaxBufferSize ];
size_t numBytesreceived;
/* Read 8k at a time and send it to all connected clients. */
while( 1 )
{
numBytesreceived = bufferevent_read( bufferEventObj, data, sizeof( data ) );
if( numBytesreceived <= 0 ) // nothing to send
{
break;
}
if( This )
{
This->OnDataReceived( data, numBytesreceived );
}
}
}
последнее, что происходит, когда я просматриваю свои данные, упаковываю их в буфер и затем делаю это на поточном временном интервале:
bufferevent_write( m_bufferEvent, buffer, bufferOffset );
Это никогда, никогда не отправляет в первый раз. Чтобы заставить его отправлять, я должен отправить второй буфер, полный данных.
Такое поведение убивает меня, и я потратил на это много часов. Есть идеи?
// ———————————————— ——-
Я наконец сдался и использовал этот взлом вместо этого … просто не было достаточно информации, чтобы сказать мне, почему libevent не записывал в сокет. Это работает просто отлично.
int result = send( m_connectionId, (const char* )buffer, bufferOffset, 0 );
Я тоже столкнулся с проблемой! Я потратил один день на эту проблему. Наконец-то я это решил.
Когда нить вы звоните event_base_dispatch
Он будет спать до тех пор, пока его не разбудит семафор. Итак, когда он спит, вы звоните bufferevent_write
, fd bufferevent добавляет в список событий, но это не будет epoll до следующего раза. Поэтому вы должны отправить семафор, чтобы разбудить поток рассылки после того, как вы позвонили bufferevent_write
, Вы можете использовать способ установки сокета пары привязки событий и добавления его в event_base
, Затем отправьте 1 байт в любое время, когда вам нужно разбудить поток рассылки.
Других решений пока нет …