/** * TraVisToClient.cpp * Copyright (C) <2009-2010> <Ulrich Kemloh> * * @section LICENSE * This file is part of JuPedSim. * * JuPedSim is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * JuPedSim 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 General Public License * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. * * @section DESCRIPTION * * * */ /********* include files ******************************************************/ #include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include "TraVisToClient.h" using namespace std; TraVisToClient::TraVisToClient(string hostname, unsigned short port) { _hostname=hostname; _port = port; _isConnected = false; createConnection(); } TraVisToClient::~TraVisToClient() { if (_isConnected) close(); } /// send datablock to the server void TraVisToClient::sendData(const char* data) { // first create a new connection, in the case the last one was lost/close if (!_isConnected) { createConnection(); //FIXME: queue messsage in a vector // msgQueue.push_back(data); return; } char msgSizeStr[10]; int msgSize = (int)strlen(data); sprintf(msgSizeStr, "%d\n", msgSize); /* check if parameters are valid */ if (NULL == data) { fprintf(stderr, "invalid message buffer!"); fprintf(stderr, "leaving sendMessage()"); _isConnected = false; return; } // do until queue empty for() /*send the length of the message*/ int msgsize = strlen(msgSizeStr); if (msgsize != send(_tcpSocket, (const char *) msgSizeStr, strlen(msgSizeStr), 0)) { fprintf(stderr, "sending message Size failed"); fprintf(stderr, "leaving sendMessage()"); _isConnected = false; return; } /* now send the message */ if (msgSize != send(_tcpSocket, (const char *) data, msgSize, 0)) { fprintf(stderr, "sending message failed"); fprintf(stderr, "leaving sendMessage()"); _isConnected = false; return; } // end do } /// close the client (end the connection) void TraVisToClient::close() { if (_isConnected) { /* all things are done, so shutdown the connection */ if (!shutdownAndCloseSocket(_tcpSocket)) { fprintf(stderr, "shutdown and close socket failed!"); stopSocketSession(); fprintf(stderr, "leaving main() with error"); return; } /* stop the socket session */ stopSocketSession(); } } void TraVisToClient::createConnection() { /* start the socket session */ if (!startSocketSession()) { fprintf(stderr, "startSocketSession() failed!"); fprintf(stderr, "socket creation failed for host [%s] on port [%d]!",_hostname.c_str(),_port); exit(EXIT_FAILURE); } /* create a new socket and connect the socket to the given service */ if (INVALID_SOCKET == (_tcpSocket = createClientSocket(_hostname.c_str(), _port))) { fprintf(stderr, "\nsocket creation failed for host [%s] on port [%d]!\n",_hostname.c_str(),_port); stopSocketSession(); exit(EXIT_FAILURE); } _isConnected = true; } /********* function definitions **************************************/ /** * This function returns an integer (unsigned long) representation * of an IP address, given as a FQN or a dotted IP address. * * @param hostName name of a host as FQN or dotted IP address * * @return The return value is an integer value of a dotted IP address if * the hostname exists (valid DNS entry) or the hostname is given * as a dotted IP address. Otherwise the function returns * @c INADDR_NONE. */ unsigned long TraVisToClient::lookupHostAddress(const char *hostName) { unsigned long addr; /* inet address of hostname */ struct hostent *host; /* host structure for DNS request */ dtrace("entering lookupHostAddress()"); if (NULL == hostName) { derror("invalid parameter"); dtrace("leaving lookupHostAddress()"); return (INADDR_NONE); } dtrace("looking for host %s", hostName); addr = inet_addr(hostName); if (INADDR_NONE == addr) { /* hostName isn't a dotted IP, so resolve it through DNS */ host = gethostbyname(hostName); if (NULL != host) { addr = *((unsigned long *) host->h_addr); } } dtrace("leaving lookupHostAddress()"); return (addr); } /******** end of function lookupHostAddress **************************/ /** * This function creates a new client internet domain socket (TCP/IP) * and connects to a service on an internet host. * * @param serverName hostname of a server * @param portNumber port number of service * * @return The function returns a socket handle if the socket can be created * and connected to a service. If an error occurred, the function * returns @c INVALID_SOCKET. */ socket_t TraVisToClient::createClientSocket(const char *serverName, unsigned short portNumber) { unsigned long ipAddress; /* internet address */ struct sockaddr_in srvAddr; /* server's internet socket address */ socket_t sock; /* file descriptor for client socket */ dtrace("entering createClientSocket()"); /* get the IP address of the server host */ if (INADDR_NONE == (ipAddress = lookupHostAddress(serverName))) { derror("lookupHostAddress() failed"); dtrace("leaving createClientSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } dtrace("trying to connect %s on port %hu", serverName, portNumber); /* create the client socket */ if (INVALID_SOCKET == (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP))) { derror("socket creation failed"); dtrace("leaving createClientSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } /* fill the server address structure */ memset(&srvAddr, 0, sizeof (srvAddr)); srvAddr.sin_family = AF_INET; srvAddr.sin_port = htons(portNumber); srvAddr.sin_addr.s_addr = ipAddress; /* try to connect to the server socket */ if (SOCKET_ERROR == connect(sock, (struct sockaddr *) & srvAddr, sizeof (srvAddr))) { derror("connect() failed"); //FIXME //closesocket(tcpSocket); dtrace("leaving createClientSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } dtrace("leaving createClientSocket()"); return (sock); } /******** end of function createClientSocket *************************/ /** * This function creates a new server internet domain socket (TCP/IP). * * @param portNumber port number on which to listen for incoming packets * * @return The function returns a socket handle if the socket can be created. * If an error occurs, the function returns @c INVALID_SOCKET. */ socket_t TraVisToClient::createServerSocket(unsigned short portNumber) { struct sockaddr_in srvAddr; /* server's internet socket address */ socket_t sock; /* file descriptor for server socket */ dtrace("entering createServerSocket()"); /* create the server socket */ if (INVALID_SOCKET == (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP))) { derror("socket creation failed"); dtrace("leaving createServerSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } /* fill the server address structure */ /* first of all, zero srvAddr, so that we have a defined status */ memset(&srvAddr, 0, sizeof (srvAddr)); srvAddr.sin_family = AF_INET; srvAddr.sin_port = htons(portNumber); srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* try to bind socket to the specified server port */ if (SOCKET_ERROR == ::bind(sock, (struct sockaddr *) & srvAddr, sizeof (srvAddr))) { derror("bind() failed!"); //FIXME: //closesocket(tcpSocket); dtrace("leaving createServerSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } if (SOCKET_ERROR == listen(sock, QUEUE_LENGTH)) { derror("listen() failed!"); shutdownAndCloseSocket(sock); dtrace("leaving createServerSocket() with INVALID_SOCKET"); return (INVALID_SOCKET); } dtrace("server started at port %hu", portNumber); dtrace("leaving createServerSocket()"); return (sock); } /******** end of function createServerSocket *************************/ /** * This function sends a message of a given size to a communication * partner via a connected socket. * * @param sock socket for sending the message * @param msg pointer to the message buffer which should be sent * @param msgSize size of the message buffer * * @return The function returns @c true if the message was sent to the server, * otherwise the function returns @c false. */ bool TraVisToClient::sendMessage(socket_t sock, const void *msg, int msgSize) { dtrace("entering sendMessage()"); /* check if parameters are valid */ if (NULL == msg) { derror("invalid message buffer!"); dtrace("leaving sendMessage()"); return (false); } if (0 >= msgSize) { derror("invalid message size %d", msgSize); dtrace("leaving sendMessage()"); return (false); } dtrace("sending message of size %d", msgSize); /* now send the message */ if (msgSize != send(sock, (const char *) msg, msgSize, 0)) { derror("sending message failed"); dtrace("leaving sendMessage()"); return (false); } dtrace("leaving sendMessage()"); return (true); } /******** end of function sendMessage ********************************/ /** * This functions receives a message from a communication partner and * stores it into a given message buffer. * The function blocks until all bytes have been successfully received, * or an error occurred. * * @param sock socket for receiving the message * @param[out] msg message buffer for receiving the message * @param msgSize size of the buffer to contain the message * * @return The function returns @c true if all things went well. Otherwise * the function returns @c false. */ bool TraVisToClient::receiveMessage(socket_t sock, void *msg, int msgSize) { char *msgPart; /* pointer to the memory for receiving the message */ int toReceive; /* number of bytes to receive */ int received; /* number of bytes totally received */ int nBytes; /* number of bytes currently received */ dtrace("entering receiveMessage()"); /* check if parameters are valid */ if (NULL == msg) { derror("invalid message buffer!"); dtrace("leaving receiveMessage()"); return (false); } if (0 >= msgSize) { derror("invalid message size!"); dtrace("leaving receiveMessage()"); return (false); } msgPart = (char *) msg; received = 0; dtrace("trying to receive a message of size %d", msgSize); /* start receiving bytes from server until complete message is received */ do { toReceive = msgSize - received; nBytes = recv(sock, msgPart, toReceive, 0); switch (nBytes) { case SOCKET_ERROR: /* error occurred */ derror("error during message receipt"); dtrace("leaving receiveMessage()"); return (false); case 0: /* connection has been closed */ derror("remote host has closed the connection"); dtrace("leaving receiveMessage()"); return (false); default: /* some bytes have been received */ dtrace("received %d bytes of message", nBytes); received += nBytes; msgPart += nBytes; break; } } while (received != msgSize); dtrace("received message of size %d", received); dtrace("leaving receiveMessage()"); return (true); } /******** end of function receiveMessage *****************************/ /** * This function shuts down and closes a given socket. * * @param sock socket to be closed * * @return if all things went ok, this function returns @c true, otherwise * @c false */ bool TraVisToClient::shutdownAndCloseSocket(socket_t sock) { bool status = true; dtrace("entering shutdownAndCloseSocket()"); if (SOCKET_ERROR == shutdown(sock, SHUT_RDWR)) { derror("shutdown() failed"); status = false; } //FIXME: //if (SOCKET_ERROR == closesocket(tcpSocket)) { // derror("closesocket() failed"); // status = false; //} dtrace("leaving shutdownAndCloseSocket()"); return (status); } /******** end of function shutdownAndCloseSocket *********************/ void TraVisToClient::_printErrorMessage(void) { } #ifdef _WIN32 /** * This function initializes the Win32 Socket API. * * @return if all things went ok, this function returns @c true, otherwise * @c false */ bool TraVisToClient::_startWin32SocketSession(void) { WORD requestedVersion; WSADATA wsaData; dtrace("entering _startWin32SocketSession()"); requestedVersion = MAKEWORD(WS_MAJOR_VERSION, WS_MINOR_VERSION); if (0 != WSAStartup(requestedVersion, &wsaData)) { derror("WSAStartup() failed"); dtrace("leaving _startWin32SocketSession() with error"); return (false); } /* Confirm that the Windows Socket DLL supports 1.1. */ /* Note that if the DLL supports versions greater */ /* than 1.1 in addition to 1.1, it will still return */ /* 1.1 in wVersion since that is the version we */ /* requested. */ if (WS_MINOR_VERSION != LOBYTE(wsaData.wVersion) || WS_MAJOR_VERSION != HIBYTE(wsaData.wVersion)) { derror("Windows Socket DLL does not support the requested version"); _stopWin32SocketSession(); dtrace("leaving _startWin32SocketSession() with error"); return (false); } WSASetLastError(0); /* reset the error code */ dtrace("leaving _startWin32SocketSession()"); return (true); } /******** end of function _startWin32SocketSession *******************/ /** * This function terminates the Win32 Socket API. * No future API calls are allowed. */ void TraVisToClient::_stopWin32SocketSession(void) { dtrace("entering _stopWin32SocketSession()"); if (SOCKET_ERROR == WSACleanup()) { derror("WSACleanup() failed"); } dtrace("leaving _stopWin32SocketSession()"); return; } /******** end of function _stopWin32SocketSession ********************/ #endif /* _WIN32 */