diff -r c892c53c664e -r 9d2210c8eed2 connectivity/com.nokia.tcf/native/TCFNative/TCFCommTCP/TcpComm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/connectivity/com.nokia.tcf/native/TCFNative/TCFCommTCP/TcpComm.cpp Mon Apr 06 15:18:48 2009 -0500 @@ -0,0 +1,666 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ +// TcpComm.cpp: implementation of the CTcpComm class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "TcpComm.h" +//#include "pn_const.h" +//#include "OSTConstants.h" +#include "Connection.h" + +#ifdef _DEBUG +static char sTcpLogMsg[3000]; +#endif +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CTcpComm::CTcpComm() +{ +#ifdef _DEBUG + if (gDoLogging) + { + FILE* f = fopen("c:\\tcf\\tcpcommlog.txt", "at"); + fprintf(f, "CTcpComm::CTcpComm() (default constructor)\n"); + fclose(f); + } +#endif + m_socket = INVALID_SOCKET; + m_timeOut.tv_sec = TIMEOUT_SEC(DEFAULT_SOCKET_TIMEOUT); + m_timeOut.tv_usec = TIMEOUT_USEC(DEFAULT_SOCKET_TIMEOUT); + + m_hSocketEvent = WSA_INVALID_EVENT; +} + +CTcpComm::CTcpComm(ConnectData* connectSettings, DWORD connectionId, CBaseProtocol* protocol) +{ +#ifdef _DEBUG + if (gDoLogging) + { + FILE* f = fopen("c:\\tcf\\tcpcommlog.txt", "at"); + fprintf(f, "connectSettings=%x connectionId=%d, protocol=%x\n", connectSettings, connectionId, protocol); + fclose(f); + } +#endif + m_connId = connectionId; + m_Protocol = protocol; + + m_ConnectSettings = new ConnectData(); + memcpy(m_ConnectSettings, connectSettings, sizeof(ConnectData)); + +#if (defined(LOG_COMM) || defined(LOG_PROCCOMM)) && defined(_DEBUG) + if (gDoLogging) + { + m_CommDebugLog = new TCDebugLog("TCF_Comm", connectionId, 2000L); + m_ProcDebugLog = new TCDebugLog("TCF_CommP", connectionId, 2000L); + } +#endif + m_socket = INVALID_SOCKET; + m_timeOut.tv_sec = TIMEOUT_SEC(DEFAULT_SOCKET_TIMEOUT); + m_timeOut.tv_usec = TIMEOUT_USEC(DEFAULT_SOCKET_TIMEOUT); + + m_hSocketEvent = WSA_INVALID_EVENT; +} +CTcpComm::~CTcpComm() +{ +#ifdef _DEBUG + if (gDoLogging) + { + FILE* f = fopen("c:\\tcf\\tcpcommlog.txt", "at"); + fprintf(f, "CTcpComm::~CTcpComm()\n"); + fclose(f); + } +#endif + if (IsConnected()) + { + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + WSACleanup(); + } + if (m_pBuffer) + delete[] m_pBuffer; + + if (m_hSocketEvent != WSA_INVALID_EVENT) + WSACloseEvent(m_hSocketEvent); + +} + +//#define USE_EVENTS +; +long CTcpComm::OpenPort() +{ + COMMLOGOPEN(); + COMMLOGS("CTcpComm::OpenPort\n"); + + long err = TCAPI_ERR_NONE; + char* ipAddress = m_ConnectSettings->tcpSettings.ipAddress; + char* ipPort = m_ConnectSettings->tcpSettings.ipPort; + // set this to set socket to non-blocking + // DWORD nonblock = 1; // non-blocking + DWORD nonblock = 0; // blocking + + COMMLOGA2("CTcpComm::OpenPort ipAddress=%s ipPort=%s\n", ipAddress, ipPort); + + WSADATA wsaData; + int wsaErr = WSAStartup(MAKEWORD(2,2), &wsaData); + if (wsaErr != 0) + { + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; +// err = -1; + } + else + { + m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == INVALID_SOCKET) + { + m_lastCommError = WSAGetLastError(); + WSACleanup(); + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; + } + else + { + if (ioctlsocket(m_socket, FIONBIO, &nonblock) == SOCKET_ERROR) + { + m_lastCommError = WSAGetLastError(); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + WSACleanup(); + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; + } + else + { + int i = SO_MAX_MSG_SIZE; + // set socket options + BOOL keepAlive = TRUE; + setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (const char*)&keepAlive, sizeof(BOOL)); + int sockRecvSize = MAX_TCP_MESSAGE_BUFFER_LENGTH;//(256*1024L); + setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (const char*)&sockRecvSize, sizeof(int)); + int sockSendSize = (64*1024L); + setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (const char*)&sockSendSize, sizeof(int)); + WSAGetLastError(); // ignore error for now + int gotsockRecvSize, optLen=sizeof(int); + getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&gotsockRecvSize, &optLen); + WSAGetLastError(); // ignore error for now + // connect + WORD wPort = atoi(ipPort); + m_clientService.sin_family = AF_INET; + m_clientService.sin_addr.S_un.S_addr = inet_addr(ipAddress); + m_clientService.sin_port = htons(wPort); + if (connect(m_socket, (SOCKADDR*)&m_clientService, sizeof(m_clientService)) == SOCKET_ERROR) + { + int wsaErr = WSAGetLastError(); + // socket is non-blocking + if (wsaErr != WSAEWOULDBLOCK) + { + m_lastCommError = wsaErr; + + closesocket(m_socket); + m_socket = INVALID_SOCKET; + WSACleanup(); + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; + } + else // WSAEWOULDBLOCK error returned + { + // WSAEWOULDBLOCK use select now + fd_set readfds, writefds, exceptfds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(m_socket, &readfds); + FD_SET(m_socket, &writefds); + FD_SET(m_socket, &exceptfds); + + int selRes = 0; + while(1) + { + selRes = select(0, &readfds, &writefds, &exceptfds, &m_timeOut); + if (selRes == SOCKET_ERROR) + { + wsaErr = WSAGetLastError(); + if (wsaErr != WSAEWOULDBLOCK) + { + // real error + m_lastCommError = wsaErr; + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + WSACleanup(); + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; + } + // else do another select + } + else if (selRes > 0)// select OK + { + m_lastCommError = 0; + m_isConnected = true; + break; // done + } + else + { + // timed out + m_lastCommError = WSAGetLastError(); + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + WSACleanup(); + err = TCAPI_ERR_WHILE_CONFIGURING_MEDIA; + } + } + } + } + else // connect return OK + { + m_lastCommError = 0; + m_isConnected = true; + } + } + } + } + if (err == TCAPI_ERR_NONE) + { + // we are connected + m_numberBytes = 0; + m_pBuffer = new BYTE[MAX_TCP_MESSAGE_BUFFER_LENGTH]; + +#ifdef USE_EVENTS + // create an event for the socket closing + m_hSocketEvent = WSACreateEvent(); + ::WSAEventSelect(m_socket, m_hSocketEvent, FD_CLOSE); + // above call sets socket to non-blocking + // cannot reset to blocking after using WSAEventSelect + // thus this ioctlsocket call will fail + ioctlsocket(m_socket, FIONBIO, &nonblock); +#endif + } + + COMMLOGCLOSE(); + return err; +} + +long CTcpComm::ClosePort() +{ + COMMLOGOPEN(); + COMMLOGS("CTcpComm::ClosePort\n"); + + long err = TCAPI_ERR_NONE; + + if (!IsConnected()) + { + err = TCAPI_ERR_MEDIA_NOT_OPEN; + } + else + { + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + WSACleanup(); + + delete[] m_pBuffer; + m_pBuffer = NULL; + + if (m_hSocketEvent != WSA_INVALID_EVENT) + { + WSACloseEvent(m_hSocketEvent); + m_hSocketEvent = WSA_INVALID_EVENT; + } + } + + COMMLOGCLOSE(); + return err; +} + +long CTcpComm::PollPort(DWORD &outSize) +{ + long err = TCAPI_ERR_NONE; + DWORD numBytes = 0; + outSize = 0; + + if (!IsConnected()) + return TCAPI_ERR_MEDIA_NOT_OPEN; + +#ifdef USE_EVENTS + int ret = ::WSAWaitForMultipleEvents(1, &m_hSocketEvent, FALSE, 0, FALSE); + if (ret == WSA_WAIT_EVENT_0) + { + ::WSAResetEvent(m_hSocketEvent); + err = TCAPI_ERR_COMM_ERROR; + m_lastCommError = WSAESHUTDOWN; + return err; + } +#endif + int sockErr = 0; int optLen = sizeof(int); + int getErr = getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char*)&sockErr, &optLen); + if (getErr == 0) + { + if (sockErr) + { + err = TCAPI_ERR_COMM_ERROR; + m_lastCommError = sockErr; + return err; + } + } + + fd_set readfds, writefds, exceptfds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(m_socket, &readfds); + FD_SET(m_socket, &writefds); + FD_SET(m_socket, &exceptfds); + + bool portReady = false; + { + TIMEVAL pollTimeout = {0,0}; // just poll the status + int selErr = select(0, &readfds, 0, 0, &pollTimeout); + if (selErr > 0) + { + if (FD_ISSET(m_socket, &readfds)) + { + m_lastCommError = 0; + portReady = true; + } + } + else if (selErr == SOCKET_ERROR) + { + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + } + + if (portReady) + { + // read was signaled as ready + int recvRet = recv(m_socket, (char*)&m_pPeekBuffer, sizeof(m_pPeekBuffer), MSG_PEEK); + if (recvRet > 0) + { + if (ioctlsocket(m_socket, FIONREAD, &numBytes) == 0) + { + m_lastCommError = 0; + outSize = numBytes; + } + else // SOCKET_ERROR + { + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + } + else if (recvRet == 0) + { + // read was signalled as ready but recv=0 signals that remote shutdown + m_lastCommError = WSAESHUTDOWN; + err = TCAPI_ERR_COMM_ERROR; + } + else + { + // SOCKET_ERROR: error on recv other than a shutdown + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + } + return err; +} + +long CTcpComm::ReadPort(DWORD inSize, void *outData, DWORD &outSize) +{ + long err = TCAPI_ERR_NONE; + DWORD numBytes = 0; + outSize = 0; + + if (!IsConnected()) + return TCAPI_ERR_MEDIA_NOT_OPEN; + + if (ioctlsocket(m_socket, FIONREAD, &numBytes) == 0) + { + if (numBytes > inSize) + numBytes = inSize; + int res = recv(m_socket, (char*)outData, numBytes, 0); + if (res == SOCKET_ERROR) + { + long commErr = WSAGetLastError(); + if ((DWORD)commErr != m_lastCommError) + { + m_lastCommError = commErr; + } + err = TCAPI_ERR_COMM_ERROR; + } + else if (res == 0) + { + // recv=0 --> connection closed + m_lastCommError = WSAESHUTDOWN; + err = TCAPI_ERR_COMM_ERROR; + } + else + { + m_lastCommError = 0; + outSize = numBytes; + } + } + else + { + // SOCKET_ERROR on ioctlsocket + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + return err; +} +long CTcpComm::ProcessBuffer(CConnection* pConn, CRegistry* pRegistry, long& numberProcessed) +{ + + long err = TCAPI_ERR_NONE; + long routingErr = TCAPI_ERR_NONE; + + if (!IsConnected()) + return TCAPI_ERR_MEDIA_NOT_OPEN; + + if (!m_Protocol) + return TCAPI_ERR_UNKNOWN_MEDIA_TYPE; + + DWORD protocolHeaderLength = m_Protocol->GetHeaderLength(); + + // fill buffer + if (m_numberBytes < MAX_TCP_MESSAGE_BUFFER_LENGTH) + { + DWORD outLen = MAX_TCP_MESSAGE_BUFFER_LENGTH - m_numberBytes; + BYTE* ptr = &m_pBuffer[m_numberBytes]; + err = ReadPort(outLen, ptr, outLen); + if (err == TCAPI_ERR_NONE && outLen > 0) + { + m_numberBytes += outLen; + } + } + + // now process buffer but only for complete messages + if (err == TCAPI_ERR_NONE) + { + if (m_numberBytes >= protocolHeaderLength) + { + BYTE* ptr = m_pBuffer; + long bytesRemaining = m_numberBytes; + long usedLen = 0; + bool done = false; + + while (!done) + { + DWORD fullMessageLength = bytesRemaining; + DWORD rawLength = 0; + BYTE* fullMessage = ptr; + BYTE* rawMessage = ptr; + BYTE msgId = 0; + if (m_Protocol->DecodeMessage(fullMessage, fullMessageLength, msgId, rawMessage, rawLength)) + { + err = PreProcessMessage(msgId, fullMessageLength, fullMessage); + if (err != TCAPI_ERR_NONE) + { + PROCLOGOPEN(); + PROCLOGA1("CTcpComm::ProcessBuffer Notify err = %x\n", err); + PROCLOGCLOSE(); + // notify all clients right now + pConn->NotifyClientsCommError(err, false, 0); + err = TCAPI_ERR_NONE; + } +#ifdef _DEBUG + int reallen = fullMessageLength; + if (reallen > 50) reallen = 50; + char msg[6]; + msg[0] = '\0'; + + sTcpLogMsg[0] = '\0'; + if (reallen > 0) + { + sTcpLogMsg[0] = '\0'; + for (int i = 0; i < reallen; i++) + { + sprintf(msg, "%02.2x ", ptr[i]); + strcat(sTcpLogMsg, msg); + } + } +#endif + PROCLOGOPEN(); + PROCLOGA5("CTcpComm::ProcessBuffer - RouteMesssage pRegistry = %x id=%x len=%d len=%d\n msg=%s\n", pRegistry, msgId, fullMessageLength, rawLength, sTcpLogMsg); + PROCLOGCLOSE(); + + err = pRegistry->RouteMessage(msgId, fullMessage, fullMessageLength, rawMessage, rawLength); + if (err != TCAPI_ERR_NONE) routingErr = err; // saved for future + + numberProcessed++; + usedLen += fullMessageLength; + bytesRemaining -= fullMessageLength; + ptr += fullMessageLength; + if (bytesRemaining < protocolHeaderLength) + done = true; + } + else + { + done = true; + } + } + DeleteMsg(usedLen); + } + } + + if (routingErr == TCAPI_ERR_NONE) + return err; + else + return routingErr; +} + + +long CTcpComm::SendDataToPort(DWORD inSize, const void* inData) +{ + COMMLOGOPEN(); + COMMLOGS("CTcpComm::SendDataToPort\n"); + + long err = TCAPI_ERR_NONE; + + if (!IsConnected()) + { + COMMLOGCLOSE(); + return TCAPI_ERR_MEDIA_NOT_OPEN; + } + +#ifdef USE_EVENTS + int ret = ::WSAWaitForMultipleEvents(1, &m_hSocketEvent, FALSE, 0, FALSE); + if (ret == WSA_WAIT_EVENT_0) + { + ::WSAResetEvent(m_hSocketEvent); + err = TCAPI_ERR_COMM_ERROR; + m_lastCommError = WSAESHUTDOWN; + COMMLOGCLOSE(); + return err; + } +#endif + int sockErr = 0; int optLen = sizeof(int); + int getErr = getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char*)&sockErr, &optLen); + if (getErr == 0) + { + if (sockErr) + { + err = TCAPI_ERR_COMM_ERROR; + m_lastCommError = sockErr; + COMMLOGCLOSE(); + return err; + } + } + + fd_set readfds, writefds, exceptfds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(m_socket, &readfds); + FD_SET(m_socket, &writefds); + FD_SET(m_socket, &exceptfds); + + COMMLOGS("CTcpComm::SendDataToPort select\n"); + bool portReady = false; + { + int selErr = select(0, &readfds, &writefds, &exceptfds, &m_timeOut); + if (selErr > 0) + { + if (FD_ISSET(m_socket, &writefds)) + { + m_lastCommError = 0; + portReady = true; + } + } + else if (selErr == SOCKET_ERROR) + { + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + else if (selErr == 0) // timeout + { + m_lastCommError = WSAGetLastError(); + err = TCAPI_ERR_COMM_ERROR; + } + } + COMMLOGA1("CTcpComm::SendDataToPort portReady=%d\n", portReady); + if (portReady) + { + COMMLOGS("CTcpComm::SendDataToPort send start\n"); + // loop until all bytes are sent + DWORD bytesRemaining = inSize; + DWORD nSent = 0; + char* unsent = (char*)inData; + while (bytesRemaining) + { + nSent = send(m_socket, unsent, bytesRemaining, 0); + if (nSent == SOCKET_ERROR) + { + int wsaErr = WSAGetLastError(); + // ignore "would block" errors + if (wsaErr != WSAEWOULDBLOCK) + { + // TODO: error handling + m_lastCommError = wsaErr; + err = TCAPI_ERR_COMM_ERROR; + break; + } + } + else + { + m_lastCommError = 0; + unsent += nSent; + bytesRemaining -= nSent; + } + } // end while + COMMLOGS("CTcpComm::SendDataToPort send done\n"); +#ifdef _DEBUG + BYTE* ptr = (BYTE*)inData; + long numBytes = (inSize > 20) ? 20 : inSize; + char msg[200]; + sprintf(msg, "CTcpComm::SendDataToPort data = "); + for (int i = 0; i < numBytes; i++) + { + sprintf(msg, "%s %02.2x", msg, ptr[i]); + } + sprintf(msg, "%s\n", msg); + COMMLOGS(msg); +#endif + } + + COMMLOGCLOSE(); + return err; +} + +void CTcpComm::DeleteMsg(DWORD inMsgLength) +{ + // inMsgLength includes header + // delete from beginning of buffer + if (inMsgLength == 0) + return; + if (m_numberBytes > 0 && m_numberBytes >= inMsgLength) + { + size_t moveLen = m_numberBytes - inMsgLength; + if (moveLen > 0) + memcpy(&m_pBuffer[0], &m_pBuffer[inMsgLength], moveLen); + m_numberBytes -= inMsgLength; + } +} +bool CTcpComm::IsConnectionEqual(ConnectData* pConn) +{ + if ((strcmp(pConn->tcpSettings.ipAddress, m_ConnectSettings->tcpSettings.ipAddress) == 0) && + (strcmp(pConn->tcpSettings.ipPort, m_ConnectSettings->tcpSettings.ipPort) == 0)) + { + return true; + } + else + { + return false; + } +} +