ctpmanager.cpp

来自「funambol window mobile客户端源代码」· C++ 代码 · 共 898 行 · 第 1/2 页

CPP
898
字号
/*
 * Funambol is a mobile platform developed by Funambol, Inc. 
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 * 
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 * 
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 * 
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include <winsock2.h>
#include "Ws2tcpip.h"
#include "base/Log.h"
#include "base/util/utils.h"
#include "CTPManager.h"
#include "CTPParam.h"




// Init static pointer.
CTPManager* CTPManager::pinstance = NULL;


/**
 * Method to create the sole instance of CTPManager
 */
CTPManager* CTPManager::getInstance() {

    if (pinstance == NULL) {
        pinstance = new CTPManager;
    }
    return pinstance;
}


/**
 * Constructor: reads the CTPConfig from registry and init members.
 */
CTPManager::CTPManager() : config(APPLICATION_URI) {

    // Read config from registry
    config.readCTPConfig();
    LOG.debug("CTP config read");

    ctpSocket        = NULL;
    ctpThread        = NULL;
    receiveThread    = NULL;
    heartbeatThread  = NULL;
    cmdTimeoutThread = NULL;
    receivedMsg      = NULL;
    ctpState         = CTP_STATE_DISCONNECTED;
    leaving          = false;

    totalBytesSent     = 0;
    totalBytesReceived = 0;
}


/**
 * Denstructor: stop any active thread, and close socket
 * connection if still active.
 */
CTPManager::~CTPManager() {
    
    stopThread(ctpThread);
    stopThread(receiveThread);
    stopThread(heartbeatThread);
    stopThread(cmdTimeoutThread);

    closeConnection();

    if (receivedMsg) {
        delete receivedMsg;
    }
}


/**
 * Starts the CTP process.
 * Creates the main CTP thread, passing handle stpThread (NULL if not created)
 * @param stpThread  handle of STPThread: the CTPThread started here must
 *                   wait until the STPThread has finished
 * @return           handle of the ctpThread started
 */
HANDLE CTPManager::startCTP(HANDLE stpThread) {

    if (ctpThread) {
        stopThread(ctpThread);
    }

    setCtpState(CTP_STATE_DISCONNECTED);
    leaving = false;
    totalBytesSent     = 0;
    totalBytesReceived = 0;

    ctpThread = CreateThread(NULL, 0, ctpWorker, (LPVOID*)stpThread, 0, NULL);
    if (!ctpThread) {
        LOG.error("Error creating CTP thread: code 0x%08x", GetLastError());
    }
    return ctpThread;
}



/**
 * Stops the CTP process.
 * 1. terminates the heartbeat thread if running, to avoid sending
 *    more READY msg at this point. 
 * 2. starts the receiving thread if not already running, to catch 
 *    the last OK msg from the Server
 * 3. sends the BYE msg
 * 4. waits for the OK msg, in case of timeout or error the 
 *    receive thread is terminated
 *
 * @return  0 if ctp closed successfully
 *          1 if ctpThread not running
 *          2 if socket connection not available
 *          3 if OK msg from the Server is not received (connection closed after a timeout)
 *         -1 if errors occurred creating the receive thread
 *         -2 if errors occurred waiting on receiveThread
 *          
 */
int CTPManager::stopCTP() {

    if (!ctpThread) {
        LOG.debug("No CTP thread available -> exiting.");
        return 1;
    }
    if (!ctpSocket) {
        LOG.debug("No socket connection -> exiting.");
        return 2;
    }

    int ret = 0;
    LOG.info("Closing CTP connection...");


    // Terminate immediately the heartbeat and cmdTimeout threads to avoid sending 
    // any READY msg now. Keep receiveThread alive, to receive the last OK msg.
    if (stopThread(heartbeatThread)) {
        LOG.debug("heartbeatThread killed");
    }
    if (stopThread(cmdTimeoutThread)) {
        LOG.debug("cmdTimeoutThread killed");
    }


    // Start thread to receive messages from Server if not running
    // If client authenticated, receiveThread is already running
    if (!receiveThread) {
        receiveThread = CreateThread(NULL, 0, receiveWorker, (LPVOID*)ctpSocket, 0, NULL);
        if (!receiveThread) {
            LOG.error("Error creating receive thread: code 0x%08x", GetLastError());
            return -1;
        }
        // Just to be sure the receiveWorker has reached the 'recv' state
        Sleep(1000);
    }


    // Set leaving to true, so receive thread will exit after the OK msg.
    leaving = true;
    setCtpState(CTP_STATE_CLOSING);


    //
    // Send the BYE message
    //
    LOG.info("Sending [BYE] message...");
    if (sendByeMsg()) {
        LOG.error("Error sending the BYE message");
        goto finally;
    }


    //
    // Wait for OK msg: receive thread should exit after the last OK
    // message sent by Server - timeout = ctpCmdTimeout (60sec).
    //
    int timeout = config.getCtpCmdTimeout();
    if (!timeout) {
        timeout = 60;
    }
    DWORD waitResult = WaitForSingleObject(receiveThread, timeout * 1000);
    switch (waitResult) {

        // Thread exited normally or abandoned -> ok
        case WAIT_ABANDONED:
            LOG.debug("receiveThread abandoned");
        case WAIT_OBJECT_0: {
            DWORD exitcode = 0;
            GetExitCodeThread(receiveThread, &exitcode);
            LOG.debug("receiveThread ended with code %d", exitcode);
            ret = exitcode;
            break;
        }

        // Timeout: kill thread -> out.
        case WAIT_TIMEOUT: {
            LOG.debug("Timeout - receiveThread will now be terminated");
            TerminateThread(receiveThread, 1);
            ret = 3;
            break;
        }

        // Some error occurred (case WAIT_FAILED)
        default: {
            LOG.debug("Wait error on receiveThread");
            TerminateThread(receiveThread, 1);
            ret = -2;
            break;
        }
    }
    CloseHandle(receiveThread);
    receiveThread = NULL;


finally:

    // Close them if still running...
    if (stopThread(receiveThread)) {
        LOG.debug("receiveThread killed");
    }
    if (stopThread(ctpThread)) {
        LOG.debug("ctpThread killed");
    }

    //
    // Close socket connection
    //
    closeConnection();

    return ret;
}




/**
 * Opens the socket connection.
 * 1. Initialize Winsock
 * 2. Get the address of the host (use getaddrinfo())
 * 3. Create a TCP/IP stream socket
 * 4. Connect to the server
 *
 * @return  0 if no errors
 */
int CTPManager::openConnection(){

    DWORD errorCode = 0;
    int ret = 0;

    if (ctpSocket) {
        // If an old CTP session is up, here we close threads and bring 
        // down socket: we want to keep the latest CTP session
        closeConnection();
    }

    LOG.info("--- Starting a new SOCKET connection ---");
    ctpState = CTP_STATE_CONNECTING;
    leaving  = false;
    totalBytesSent     = 0;
    totalBytesReceived = 0;

    //
    // Initialize Winsock
    //
    WORD versionRequested = MAKEWORD(1, 1);
	WSADATA wsaData;
    ret = WSAStartup(versionRequested, &wsaData);
    if (ret != NO_ERROR) {
        errorCode = WSAGetLastError();
        LOG.error("SOCKET WSAStartup() error %d: %s", errorCode, createErrorMsg(errorCode));
        ctpState = CTP_STATE_DISCONNECTED;
        return -1;
    }

    // Check if version is correct 
    if (wsaData.wVersion != versionRequested) {
        LOG.error("WinSock version not supported: %d (%d expected)", wsaData.wVersion, versionRequested);
        ctpState = CTP_STATE_DISCONNECTED;
        return -1;
	}


    //
    // Find the server
    //
    LOG.debug("Find the server address...");
    struct addrinfo aiHints;
    struct addrinfo *aiList = NULL;
    
    // Setup the hints address info structure
    // which is passed to the getaddrinfo() function
    memset(&aiHints, 0, sizeof(aiHints));
    aiHints.ai_family   = AF_INET;				// Address family
    aiHints.ai_socktype = SOCK_STREAM;			// Socket type
    aiHints.ai_protocol = IPPROTO_TCP;		    // Protocol
    aiHints.ai_flags    = AI_CANONNAME;         // To get the canonical name

    const char* hostName = config.getUrlTo().c_str();
    char port[10];
    sprintf(port, "%d", config.getCtpPort());
    LOG.info("HOSTNAME = '%s'  PORT = '%s'", hostName, port);

    // Resolve the host name.
    ret = getaddrinfo(hostName, port, &aiHints, &aiList);
    if (ret) {
		//lastErrorCode = ERR_HOST_NOT_FOUND;
        setErrorF(ERR_HOST_NOT_FOUND,"getaddrinfo() failed: %s", gai_strerror(ret));
        LOG.error("getaddrinfo() failed: %s", gai_strerror(ret));
        ret = -2;
        goto finally;
    }

    struct addrinfo *addr = aiList;

    // Loop on possible addresses
    while (addr != NULL) {
        //
        // Create a TCP/IP stream socket
        //
        LOG.debug("Create SOCKET connection...");
        ctpSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (ctpSocket == INVALID_SOCKET) {
            if (addr->ai_next != NULL) {
                addr=addr->ai_next;        // take next address
                continue;
            }
            else {
                errorCode = WSAGetLastError();
                LOG.error("SOCKET socket() error %d: %s", errorCode, createErrorMsg(errorCode));
                ret = -3;
                goto finally;
            }
        }

        //
        // Connect to the server
        //
        LOG.debug("Connecting to '%s'...", hostName);
        ret = connect(ctpSocket, addr->ai_addr, addr->ai_addrlen);
        if (ret == SOCKET_ERROR) {
            if (addr->ai_next != NULL) {
                addr=addr->ai_next;        // take next address
                closesocket(ctpSocket);
                continue;
            }
            else {
                errorCode = WSAGetLastError();
                if (errorCode == WSAECONNREFUSED) {
                    LOG.error("SOCKET connect() error: Server not responding at %s:%s", hostName, port);
                }
                else {
                    LOG.error("SOCKET connect() error %d: %s", errorCode, createErrorMsg(errorCode));
                }
                ret = -4;
                goto finally;
            }
        }
        LOG.info("Succesfully connected to %s!", addr->ai_canonname);
        ctpState = CTP_STATE_CONNECTED;
        break;
    }

finally:
    if (aiList) {
        freeaddrinfo(aiList);
    }
    if (ret != 0) {
        ctpState = CTP_STATE_DISCONNECTED;
    }
    return ret;
}



/**
 * Closes the socket connection.
 * @return 0  if no errors
 */
int CTPManager::closeConnection(){

    int ret = 0;
    if (ctpSocket) {
        ret = closesocket(ctpSocket);
        WSACleanup();
        ctpSocket = NULL;
        LOG.info("Socket connection closed");
    }

    // Just to be sure: close all ctp threads if still running
    stopThread(cmdTimeoutThread);
    stopThread(heartbeatThread);
    stopThread(receiveThread);
    
    ctpState = CTP_STATE_DISCONNECTED;
    LOG.debug("Total number of bytes sent = %d",     totalBytesSent);
    LOG.debug("Total number of bytes received = %d", totalBytesReceived);
    totalBytesSent     = 0;
    totalBytesReceived = 0;

    return ret;
}



/**
 * Sends an AUTH message to the Server.
 * A CTPMessage is filled with parameters from CTPConfig.
 * 'sendMsg()' method is used to send the message with the ctpSocket.
 * @return 0 if no errors
 */
int CTPManager::sendAuthMsg(){

    LOG.debug("Creating AUTH msg...");
    ctpState = CTP_STATE_AUTHENTICATING;

    // Fill CTPMessage members
    CTPMessage authMsg;
    authMsg.setGenericCommand(CM_AUTH);
    authMsg.setProtocolVersion(CTP_PROTOCOL_VERSION);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?