AIO를 사용한 서버 구성시 메모리 에러
리눅스의 Posix AIO를 사용하여 간단한 서버를 만들어보았습니다.
클라이언트가 접속을 요청하면 aiocb 구조체를 동적할당하며 aio_read()함수를 호출하는 방식입니다.
그런데 동적 할당 후 aio_read()함수 실행 시 invalid argument에러를 출력합니다.
원인을 찾아보던 중 동적할당한 aiocb 구조체의 메모리를 해제한 것이 문제였습니다.
delete를 통해 메모리를 해제하는 부분을 주석처리하고 해제하는 메모리를 출력하도록 하였더니
정상동작하는 것을 확인했습니다.
delete를 통해 수동으로 메모리 해제 시 왜 문제가 발생하는지 궁금합니다.
서버 소스입니다.
/* Header */
#include
#include
#include
#include
#include
// AIO Header
#include
#include
// Custom Header
#include "header/Decl.h"
using namespace std;
/* Global variable */
SOCKET SERVERSOCKET;
/* Function declare */
void ErrorExit( const char* );
// Function related in socket
const int CreateTCPSocket( void );
void InitTCPSocket( const SOCKET, const int );
// Function related in AIO
void SetAiocb( struct aiocb*, const SOCKET );
void AioHandler( union sigval );
/* Main function */
int main( int argc, char *argv[] )
{
/*
if ( argc != 2 )
{
cout << "Usage: " << argv[0] << " " << endl;
exit(1);
}*/
// Socket
SOCKET sock = CreateTCPSocket();
// InitTCPSocket( sock, atoi( argv[1] ) );
InitTCPSocket( sock, 8888 );
SERVERSOCKET = sock;
struct aiocb cb;
SetAiocb( &cb, sock );
if ( aio_read( &cb ) < 0 )
ErrorExit( "aio_read() failed in main()" );
while ( true )
{
cout << "I'm sleeping" << endl;
sleep(100);
}
delete[] reinterpret_cast< char* >( const_cast< void* >( cb.aio_buf ) );
return 0;
}
/* Function definition */
void ErrorExit( const char *str )
{
cout << "[Error] " << str << endl;
exit(1);
}
// Function related in socket
const int CreateTCPSocket( void )
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock < 0 )
ErrorExit("[Error] socket() fail");
cout << "socket() success" << endl;
return sock;
}
void InitTCPSocket( const SOCKET sock, const int port )
{
struct sockaddr_in ServerAddr; memset( &ServerAddr, 0, sizeof( ServerAddr ) );
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons( port );
ServerAddr.sin_addr.s_addr = htonl( INADDR_ANY );
if ( bind( sock, reinterpret_cast< sockaddr* >( &ServerAddr ), sizeof( ServerAddr ) ) < 0 )
ErrorExit( "bind() fail" );
cout << "bind() success" << endl;
if ( listen( sock, CONCURRENT) < 0 )
ErrorExit( "listen() fail" );
cout << "listen() success" << endl;
}
// Function related in AIO
void SetAiocb( struct aiocb *cb, const SOCKET fd )
{
/*
* Type of cb->aiobuff is volatile void*.
* but return value of new char[ BUFFSIZE ] is char*.
* thus reinterpret_cast is used
*/
cb->aio_buf = reinterpret_cast< void* >( new char[ BUFFSIZE ] );
cb->aio_nbytes = BUFFSIZE;
cb->aio_fildes = fd;
cb->aio_offset = 0;
cb->aio_sigevent.sigev_notify = SIGEV_THREAD;
cb->aio_sigevent.sigev_notify_function = AioHandler;
cb->aio_sigevent.sigev_notify_attributes = NULL;
cb->aio_sigevent.sigev_value.sival_ptr = cb;
}
void AioHandler( union sigval sig)
{
struct aiocb *cb = reinterpret_cast< aiocb* >( sig.sival_ptr );
SOCKET sock = cb->aio_fildes;
if ( sock == SERVERSOCKET )
{
struct sockaddr_in ClientAddr;
socklen_t ClientAddrLen = sizeof( ClientAddr );
memset( &ClientAddr, 0, ClientAddrLen );
SOCKET ClientSock = accept( sock, reinterpret_cast< sockaddr* >( &ClientAddr ), &ClientAddrLen );
if ( ClientSock < 0 )
cout << "The client attempted to connect but the accept() failed";
else
cout << "Connected" << endl;
struct aiocb *ClientAio = new aiocb;
SetAiocb( ClientAio, ClientSock );
if ( aio_read( cb ) < 0 )
ErrorExit("Error occurred! aio_read( cb ) failed in ServerSocket AioHandler");
if ( aio_read( ClientAio ) < 0 )
{
cout << "==========Failed CB Info==========" << endl;
cout << "ClientAio->aio_buf " << sizeof( ClientAio->aio_buf ) << endl;
cout << "ClientAio->aio_nbytes " << ClientAio->aio_nbytes << endl;
cout << "ClientAio->aio_fildes " << ClientAio->aio_fildes << endl;
cout << "ClientAio->aio_offset " << ClientAio->aio_offset << endl;
cout << "ClientAio->aio_sigevent.sigev_notify " << ClientAio->aio_sigevent.sigev_notify << endl;
cout << "CientAio->aio_sigevent.sigev_notify_function " << ClientAio->aio_sigevent.sigev_notify_function << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_attributes " << ClientAio->aio_sigevent.sigev_notify_attributes << endl;
cout << "ClientAio->aio_sigevent.sigev_value.sival_ptr " << ClientAio->aio_sigevent.sigev_value.sival_ptr << endl;
close( ClientSock );
// delete ClientAio;
cout << "Error occurred! aio_read( ClientAio ) failed in ServerSocket AioHandler" << endl;
}
else
{
cout << "==========Success CB Info==========" << endl;
cout << "ClientAio->aio_buf " << sizeof( ClientAio->aio_buf ) << endl;
cout << "ClientAio->aio_nbytes " << ClientAio->aio_nbytes << endl;
cout << "ClientAio->aio_fildes " << ClientAio->aio_fildes << endl;
cout << "ClientAio->aio_offset " << ClientAio->aio_offset << endl;
cout << "ClientAio->aio_sigevent.sigev_notify " << ClientAio->aio_sigevent.sigev_notify << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_function " << ClientAio->aio_sigevent.sigev_notify_function << endl;
cout << "ClientAio->aio_sigevent.sigev_notify_attributes " << ClientAio->aio_sigevent.sigev_notify_attributes << endl;
cout << "ClientAio->aio_sigevent.sigev_value.sival_ptr " << ClientAio->aio_sigevent.sigev_value.sival_ptr << endl;
}
}
else
{
cout << "Client process routing start " << sock << endl;
/*
* Type of cb->aiobuff is volatile void*.
* but i do not need volatile. i just need socket buffer.
* thus cast volatile void* to void*
* and cast void* to char*
*/
char *buff = reinterpret_cast< char* >( const_cast< void* >( cb->aio_buf ) );
size_t bufflen = aio_return(cb);
cout << "Read data length is " << bufflen << endl;
struct Packet RecvPacket;
RecvPacket.type = buff[0];
strcpy( RecvPacket.username, &buff[1] );
strcpy( RecvPacket.password, &buff[65] );
if ( RecvPacket.type == Request::Login )
{
cout << "Client requests login" << endl;
cout << "Username: " << RecvPacket.username << endl;
cout << "Password: " << RecvPacket.password << endl;
write( sock, "Login Success", sizeof( "Login Success" ) );
}
else if ( RecvPacket.type == Request::SignUP )
{
cout << "Client requests signup" << endl;
cout << "Username: " << RecvPacket.username << endl;
cout << "Password: " << RecvPacket.password << endl;
write( sock, "Sign Up Success", sizeof( "Sign Up Success" ) );
}
else
cout << "Client sent unknown type " << RecvPacket.type << endl;
close( sock );
/* #################
* # Problem Point #
* #################
*/
// delete cb;
cout << "delete cb " << cb << endl;
cout << "Connection Close" << endl;
}
}
.
.
callback 에서 자기 자신을 없애버리는게
callback 에서 자기 자신을 없애버리는게 타당한지 모르겠습니다.
댓글 달기