/* * c't-Sim - Robotersimulator fuer den c't-Bot * * This program 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 2 of the License, or (at your * option) any later version. * 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 General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA. * */ /*! @file tcp.c * @brief TCP/IP-Kommunikation * @author Benjamin Benz (bbe@heise.de) * @date 26.12.05 */ #include "ct-Bot.h" #define USE_SEND_BUFFER #ifdef PC /* Linux with glibc: * _REENTRANT to grab thread-safe libraries * _POSIX_SOURCE to get POSIX semantics */ #ifndef WIN32 # define _REENTRANT //# define _POSIX_SOURCE #else // #define WIN32 #endif /* Hack for LinuxThreads */ #ifdef __linux__ # define _P __P #endif #include #ifdef WIN32 #include #else #include #include #include #include // for gethostbyname() #include #endif #include // for printf() and fprintf() #include // for atoi() and exit() #include // for memset() #include // for close() #include "tcp.h" #include "display.h" int tcp_sock=0; /*!< Unser TCP-Socket */ char *tcp_hostname = NULL; /*!< Hostname, auf dem ct-Sim laeuft */ #ifdef PC uint8 sendBuffer[1024]; /*!< Sendepuffer fuer ausgehende Packete */ int sendBufferPtr=0; /*!< Index in den Sendepuffer */ #endif /*! * Oeffnet eine TCP-Verbindung zum Server * @param hostname Symbolischer Name des Host, auf dem ct-Sim laeuft * @return Der Socket */ int tcp_openConnection(const char *hostname){ struct sockaddr_in servAddr; // server address int sock=0; // Socket descriptor struct hostent *he = gethostbyname(hostname); // Ueberpruefen, ob der Hostname aufgeloest werden konnte if (NULL == he) { printf("gethostbyname() failed\n"); exit(1); } // Create a stream socket for TCP if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ printf("socket() failed"); exit(1); } int flag = 1; setsockopt(sock, /* socket affected */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ (char *) &flag, /* the cast is historical cruft */ sizeof(int)); /* length of option value */ // Prepare server address structure memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure servAddr.sin_family = AF_INET; // Internet address servAddr.sin_port = htons(PORT); // Port // Die erste Adresse aus der Liste uebernehmen memcpy(&servAddr.sin_addr, *(he->h_addr_list), sizeof(servAddr.sin_addr)); // Open Connection to the server if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0){ printf("tcp_openConnection() to %s failed\n", inet_ntoa(servAddr.sin_addr)); exit(1); } return sock; } /*! * Schliesst eine TCP-Connection * @param sock Der Socket */ void tcp_closeConnection(int sock){ close(sock); #ifdef WIN32 WSACleanup(); #endif } /*! * Sende Kommando per TCP/IP im Little Endian * Diese Funktion setzt vorraus, dass die Symbole BYTE_ORDER und BIG_ENDIAN * bzw. LITTLE_ENDIAN definiert wurden. Damit dies auf Linux/Unix * funktioniert darf _POSIX_SOURCE nicht definiert werden. Fuer Windows * wird dies in der Headerdatei tcp.h erledigt. * Getestet wurde dies bisher auf folgenden Systemen: * - MacOSX (PPC, big endian) * - Gentoo Linux (hppa, big endian) * - OpenBSD (i386, little endian) * - Windows 2000 (i386, little endian mit dem MinGW) * Sollten in command_t weitere Werte mit mehr bzw. weniger als 8 bit * aufgenommen werden muss hier eine entsprechende Anpassung erfolgen. * @param cmd Zeiger auf das Kommando * @return Anzahl der gesendete Bytes */ int tcp_send_cmd(command_t *cmd){ #if BYTE_ORDER == BIG_ENDIAN command_t le_cmd; /* Kopieren des Kommandos und auf Little Endian wandeln */ memcpy(&le_cmd, cmd, sizeof(command_t)); /* Alle 16bit Werte in Little Endian wandeln */ le_cmd.data_l = cmd->data_l << 8; le_cmd.data_l |= (cmd->data_l >> 8) & 0xff; le_cmd.data_r = cmd->data_r << 8; le_cmd.data_r |= (cmd->data_r >> 8) & 0xff; le_cmd.seq = cmd->seq <<8; le_cmd.seq |= (cmd->seq >> 8) & 0xff; /* "Umdrehen" des Bitfields */ le_cmd.request.subcommand = cmd->request.subcommand >> 1; le_cmd.request.direction = (cmd->request.subcommand & 1) << 7; return tcp_write(&le_cmd, sizeof(command_t)); #else return tcp_write(cmd, sizeof(command_t)); #endif } /*! Puffert daten im Sendepuffer zwischen * @param data zeiger auf die daten * @param length Anzahl der Bytes * @return -1 wenn kein Platz mehr im Puffer ist, 0 wenn alles ok ist */ int copy2Buffer(uint8* data, int length){ int i; uint8 * ptr = data; if ((sendBufferPtr + length) > sizeof(sendBuffer)) return -1; // printf("Store %d bytes",length); // Auf dem PC kopieren wir nur alles in den Ausgangspuffer for (i=0; i< length ; i++){ sendBuffer[sendBufferPtr++]= *ptr++ ; } // printf(" %d Bytes now in buffer\n",sendBufferPtr); return 0; } /*! * Uebertrage Daten per TCP/IP * @param data Zeiger auf die Daten * @param length Anzahl der Bytes * @return 0 wenn alles ok, -1 wenn puffer voll */ int tcp_write(void* data, int length){ #ifdef USE_SEND_BUFFER return copy2Buffer(data, length); #else if (send(tcp_sock,data,length,0) != length){ printf("send() sent a different number of bytes than expected"); return -1; } return length; #endif } /*! * Lese Daten von TCP/IP-Verbindung. * Achtung: blockierend! * @param data Zeiger auf die Daten * @param length Anzahl der gewuenschten Bytes * @return Anzahl der uebertragenen Bytes */ int tcp_read(void* data, int length){ int bytesReceived=0; if ((bytesReceived = recv(tcp_sock, data, length, 0)) <= 0){ printf("recv() failed or connection closed prematurely\n"); exit(1); return -1; } return bytesReceived; } /*! * Initialisiere TCP/IP Verbindung */ void tcp_init(void){ #ifdef WIN32 WSADATA wsaData; WORD wVersionRequested; int err; wVersionRequested = MAKEWORD( 2, 0 ); // 2.0 and above version of WinSock err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { fprintf(stderr, "Couldn't not find a usable WinSock DLL.\n"); exit(1); } #endif if ((tcp_sock=tcp_openConnection(tcp_hostname)) != -1) printf ("connection to %s established on Port: %d\n", tcp_hostname, PORT); else { printf ("connection to %s failed on Port: %d\n", tcp_hostname, PORT); exit(1); } } /*! * Schreibt den Sendepuffer auf einen Schlag raus * @return -1 bei Fehlern, sonst zahl der uebertragenen Bytes */ int flushSendBuffer(void){ #ifdef USE_SEND_BUFFER // printf("Flushing Buffer with %d bytes\n",sendBufferPtr); int length=sendBufferPtr; sendBufferPtr=0; // Puffer auf jedenfall leeren if (send(tcp_sock,(char*)&sendBuffer,length,0) != length){ printf("send() sent a different number of bytes than expected"); return -1; } return length; #else return 0; #endif } #endif