connectivity/com.nokia.tcf/native/TCFNative/TCFCommTCP/TcpComm.cpp
changeset 60 9d2210c8eed2
child 366 b054461d2f85
--- /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;
+	}
+}
+