/***************************************************************************** ************************* AIRBORNE SYSTEMS ************************* ************************* COMPUTING DEVICES CANADA ************************* ****************************************************************************** Filename: CClientSocket.cpp Project: LOCATOR Purpose: C++ implementation for the LOCATOR CClientSocket class. This class provides the functionality for client sockets, which are the sockets used to reveice/send messages over an interface. Release: V1.0 Author: Denis Dumas Date: 03-Aug-1999 Modified: *****************************************************************************/ #include "vxWorks.h" #include "sockLib.h" #include "inetLib.h" #include "rtx/netdb.h" #include "string.h" #include "errno.h" #include "LOCATORconstants.h" #include "CClientSocket.h" #include "CMsgLog.h" /*--------------------------------------------------------------------------- Method Name: CClientSocket::CClientSocket() Purpose: Default constructor. Parameters In: None. Parameters Out: None. Return Values: None. Notes: None. ---------------------------------------------------------------------------*/ CClientSocket::CClientSocket() { } /*--------------------------------------------------------------------------- Method Name: CClientSocket::~CClientSocket() Purpose: Default destructor. Parameters In: None. Parameters Out: None. Return Values: None. Notes: None. ---------------------------------------------------------------------------*/ CClientSocket::~CClientSocket() { } /*--------------------------------------------------------------------------- Method Name: CClientSocket::Init() Purpose: Initializes the client socket. Parameters In: None. Parameters Out: None. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::Init() { // Always call the CSocket::Init method. if ( CSocket::Init() != SUCCESS ) return ERROR_GENERAL; // Set the options for this client socket. if ( SetOpts() != SUCCESS ) return ERROR_GENERAL; // All ok at this point. return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::Init ( int ) Purpose: Initializes the client socket. Parameters In: A valid socket file descriptor. Parameters Out: None. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::Init ( const int pSocketDescriptor ) { // Always call the CSocket::Init method. if ( CSocket::Init ( pSocketDescriptor ) != SUCCESS ) return ERROR_GENERAL; // Set the options for this client socket. if ( SetOpts() != SUCCESS ) return ERROR_GENERAL; // All ok at this point. return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::Connect() Purpose: Attempts to connect to the interface terminal. Parameters In: pTerminalName - IP address string of terminal process in dotted-decimal notation, or "localhost" to point to the loopback interface. pTerminalPort - port number for connection. Parameters Out: None. Return Values: SUCCESS on successful completion, ERROR_CONNECTION_REFUSED if connection refused, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::Connect ( const char * const pTerminalName, const unsigned short pTerminalPort ) { const char * const lMethod = "CClientSocket::Connect()"; struct sockaddr_in lSockaddr; struct hostent *lHostStruct; int lNumberOfPeriods, lStatus; char *lNextChar; bool lIsIPaddress; // At this point the socket should be initialized. if ( !cIsInitialized ) { gMsg.Log ( lMethod, "socket not initialized yet" ); gMsg.Log ( "port number", pTerminalPort ); gMsg.Log ( "terminal name", pTerminalName ); return ERROR_GENERAL; } // See if this is a "dotted-decimal" IP address by counting the // number of periods in the string. There should be 3 periods. lNumberOfPeriods = 0; lNextChar = (char *) pTerminalName; while ( ( lNextChar = strchr ( lNextChar, '.' ) ) != NULL ) { lNumberOfPeriods++; lNextChar++; } lIsIPaddress = ( lNumberOfPeriods == 3 ? true : false ); // Initialize the socket address structure. memset ( &lSockaddr, 0, sizeof ( lSockaddr ) ); lSockaddr.sin_family = AF_INET; lSockaddr.sin_port = htons ( (unsigned short) pTerminalPort ); // Get the host name... if ( lIsIPaddress ) lSockaddr.sin_addr.s_addr = inet_addr ( pTerminalName ); else { lHostStruct = gethostbyname ( /*(char *) pTerminalName*/ ); if ( lHostStruct == NULL ) { gMsg.Log ( lMethod, "gethostbyname error" ); gMsg.Log ( "errno error", strerror ( errno ) ); gMsg.Log ( "terminal name", pTerminalName ); gMsg.Log ( "port number", pTerminalPort ); return ERROR_GENERAL; } memcpy ( &lSockaddr.sin_addr, lHostStruct->h_addr, lHostStruct->h_length ); } // Now try to connect to the host server. lStatus = connect ( cSocketDescriptor, (struct sockaddr *) &lSockaddr, sizeof ( lSockaddr ) ); if ( lStatus < 0 ) { gMsg.Log ( lMethod, "connect error" ); gMsg.Log ( "errno error", strerror ( errno ) ); gMsg.Log ( "terminal name", pTerminalName ); gMsg.Log ( "port number", pTerminalPort ); if ( errno == ECONNREFUSED ) return ERROR_CONNECTION_REFUSED; return ERROR_GENERAL; } // The connection was established... return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::SetOpts() Purpose: Sets options for this client socket. Parameters In: none. Parameters Out: None. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::SetOpts() { const char * const lMethod = "CClientSocket::SetOpts()"; int lStatus; const int lBufSize = SOBUFSIZE; // Set the receive and send buffer sizes. The system default size is // 4096 bytes, and the maximum size is 1Mbyte. lStatus = setsockopt ( cSocketDescriptor, SOL_SOCKET, SO_RCVBUF, (char *) &lBufSize, sizeof ( int ) ); if ( lStatus < 0 ) { gMsg.Log ( lMethod, "setsockopt (SO_RCVBUF) error" ); gMsg.Log ( "errno error", strerror ( errno ) ); return ERROR_GENERAL; } lStatus = setsockopt ( cSocketDescriptor, SOL_SOCKET, SO_SNDBUF, (char *) &lBufSize, sizeof ( int ) ); if ( lStatus < 0 ) { gMsg.Log ( lMethod, "setsockopt (SO_SNDBUF) error" ); gMsg.Log ( "errno error", strerror ( errno ) ); return ERROR_GENERAL; } // All is ok at this point. return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::CheckClientSocket() Purpose: Calls CheckSocket from the CSocket class to check the socket for data and errors, then peeks at the socket data if there is supposed to be some. This checks for a closed connection. Parameters In: pHasData - pointer to boolean. Parameters Out: pHasData - Set to true if there is data in the socket, otherwise set to false. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::CheckClientSocket ( bool * const pHasData ) { const char * const lMethod = "CClientSocket::CheckClientSocket()"; int lNbytes, lStatus; char lPeekChar[5]; // First call the CSocket::CheckSocket to perform the basic checks. lStatus = CheckSocket ( pHasData ); if ( lStatus != SUCCESS ) return lStatus; // Now if there is data in the socket peek at it. If this call returns // without an error and with 0 bytes read, the terminal has closed the // connection. if ( *pHasData ) { lNbytes = recv ( cSocketDescriptor, lPeekChar, 1, MSG_PEEK ); if ( lNbytes < 0 ) { gMsg.Log ( lMethod, "error during recv (MSG_PEEK)" ); gMsg.Log ( "errno error", strerror ( errno ) ); return ERROR_GENERAL; } else if ( lNbytes == 0 ) { gMsg.Log ( lMethod, "terminal closed connection" ); return ERROR_CONNECTION_CLOSED; } } // All is ok. return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::Write() Purpose: Writes data to the socket. Parameters In: pBuffer - pointer to the data buffer. pBufferSize - number of bytes to write from buffer. Parameters Out: None. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::Write ( void * pBuffer, const int pBufferSize ) { const char * const lMethod = "CClientSocket::Write()"; ssize_t lNbytes; // Send the data. lNbytes = send ( cSocketDescriptor, (char *) pBuffer, pBufferSize, 0 ); if ( lNbytes < 0 ) { gMsg.Log ( lMethod, "error during send" ); gMsg.Log ( "buffer length", pBufferSize ); gMsg.Log ( "errno error", strerror ( errno ) ); return ERROR_GENERAL; } // Correct number of bytes sent? if ( lNbytes != pBufferSize ) { gMsg.Log ( lMethod, "incorrect number of bytes sent on socket" ); gMsg.Log ( "buffer length", pBufferSize ); gMsg.Log ( "bytes sent", lNbytes ); return ERROR_GENERAL; } // Successful completion. return SUCCESS; } /*--------------------------------------------------------------------------- Method Name: CClientSocket::Read() Purpose: Reads data from the socket. Parameters In: pBuffer - pointer to the data buffer. pBufferSize - number of bytes to read into pBuffer. Parameters Out: pBuffer - updated with data read from socket. Return Values: SUCCESS on successful completion, ERROR_GENERAL otherwise. Notes: None. ---------------------------------------------------------------------------*/ int CClientSocket::Read ( void * pBuffer, const int pBufferSize ) { const char * const lMethod = "CClientSocket::Read()"; ssize_t lNbytes; // Receive the message. Block until all data is read. lNbytes = recv ( cSocketDescriptor, (char *) pBuffer, pBufferSize, MSG_WAITALL ); if ( lNbytes < 0 ) { gMsg.Log ( lMethod, "error during recv" ); gMsg.Log ( "buffer length", pBufferSize ); gMsg.Log ( "errno error", strerror ( errno ) ); return ERROR_GENERAL; } // If the number of bytes is 0 then the connection has been closed. if ( lNbytes == 0 ) { gMsg.Log ( lMethod, "connection closed (0 bytes read)" ); return ERROR_CONNECTION_CLOSED; } // Correct number of bytes read? if ( lNbytes != pBufferSize ) { gMsg.Log ( lMethod, "incorrect number of bytes read" ); gMsg.Log ( "buffer length", pBufferSize ); gMsg.Log ( "bytes read", lNbytes ); return ERROR_GENERAL; } // Successful completion. return SUCCESS; }