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 + -
显示快捷键?