C++程序  |  191行  |  5.56 KB

/*
 * Copyright (c) 2011-2014, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Socket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <assert.h>
#include <netdb.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/time.h>
#include <signal.h>

CSocket::CSocket() : _iSockFd(socket(AF_INET, SOCK_STREAM, 0)), mSendFlag(0)
{
    assert(_iSockFd != -1);

    int iNoDelay = 1;
    // (see man 7 tcp)
    // Setting TCP_NODELAY allows us sending commands and responses as soon as
    // they are ready to be sent, instead of waiting for more data on the
    // socket.
    setsockopt(_iSockFd, IPPROTO_TCP, TCP_NODELAY, (char *)&iNoDelay, sizeof(iNoDelay));

    // Disable sigpipe reception on send
#   if not defined(SIGPIPE)
        // Pipe signal does not exist, there no sigpipe to ignore on send
#   elif defined(SO_NOSIGPIPE)
        const int set = 1;
        setsockopt(_iSockFd, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set));
#   elif defined(MSG_NOSIGNAL)
        // Use flag NOSIGNAL on send call
        mSendFlag = MSG_NOSIGNAL;
#   else
#       error Can not disable SIGPIPE
#   endif
}

CSocket::CSocket(int iSockId) : _iSockFd(iSockId)
{
    assert(_iSockFd != -1);
}

CSocket::~CSocket()
{
    // fd might be invalide if send had an error.
    // valgrind displays a warning if closing an invalid fd.
    if (_iSockFd != -1) {
        close(_iSockFd);
    }
}

// Socket address init
void CSocket::initSockAddrIn(struct sockaddr_in* pSockAddrIn, uint32_t uiInAddr, uint16_t uiPort) const
{
    // Fill server address
    pSockAddrIn->sin_family = AF_INET;
    pSockAddrIn->sin_port = htons(uiPort);
    pSockAddrIn->sin_addr.s_addr = uiInAddr;
    bzero(&pSockAddrIn->sin_zero, sizeof(pSockAddrIn->sin_zero));
}

// Non blocking state
void CSocket::setNonBlocking(bool bNonBlocking)
{
    int iFlags = fcntl(_iSockFd, F_GETFL, 0);

    assert(iFlags != -1);

    if (bNonBlocking) {

        iFlags |= O_NONBLOCK;
    } else {

        iFlags &= ~O_NONBLOCK;
    }
    fcntl(_iSockFd, F_SETFL, iFlags);
}

// Communication timeout
void CSocket::setTimeout(uint32_t uiMilliseconds)
{
    struct timeval tv;
    tv.tv_sec = uiMilliseconds / 1000;
    tv.tv_usec = (uiMilliseconds % 1000) * 1000;

    setsockopt(_iSockFd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
    setsockopt(_iSockFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}

// Read
bool CSocket::read(void* pvData, uint32_t uiSize)
{
    uint32_t uiOffset = 0;
    uint8_t* pucData = (uint8_t*)pvData;

    while (uiSize) {

        int32_t iAccessedSize = ::recv(_iSockFd, &pucData[uiOffset], uiSize, 0);

        switch (iAccessedSize) {
        case 0:
            // recv return value is 0 when the peer has performed an orderly shutdown.
            _disconnected = true;
            errno = ECONNRESET; // Warn the client that the client disconnected.
            return false;

        case -1:
            // errno == EINTR => The recv system call was interrupted, try again
            if (errno != EINTR) {
                return false;
            }
            break;

        default:
            uiSize -= iAccessedSize;
            uiOffset += iAccessedSize;
        }
    }
    return true;
}

// Write
bool CSocket::write(const void* pvData, uint32_t uiSize)
{
    uint32_t uiOffset = 0;
    const uint8_t* pucData = (const uint8_t*)pvData;

    while (uiSize) {

        int32_t iAccessedSize = ::send(_iSockFd, &pucData[uiOffset], uiSize, mSendFlag);

        if (iAccessedSize == -1) {
            if (errno == EINTR) {
                // The send system call was interrupted, try again
                continue;
            }

            // An error occured, forget this socket
            _disconnected = true;
            close(_iSockFd);
            _iSockFd = -1; // Avoid writing again on the same socket
            return false;
        } else {
            uiSize -= iAccessedSize;
            uiOffset += iAccessedSize;
        }
    }
    return true;
}

// Fd
int CSocket::getFd() const
{
    return _iSockFd;
}

bool CSocket::hasPeerDisconnected() {
    return _disconnected;
}