summaryrefslogtreecommitdiffstats
path: root/source/ct-Bot/pc/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/ct-Bot/pc/tcp.c')
-rw-r--r--source/ct-Bot/pc/tcp.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/source/ct-Bot/pc/tcp.c b/source/ct-Bot/pc/tcp.c
new file mode 100644
index 0000000..d654f3d
--- /dev/null
+++ b/source/ct-Bot/pc/tcp.c
@@ -0,0 +1,282 @@
+/*
+ * 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 <pthread.h>
+
+
+#ifdef WIN32
+ #include <winsock.h>
+#else
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netdb.h> // for gethostbyname()
+ #include <netinet/tcp.h>
+#endif
+
+
+#include <stdio.h> // for printf() and fprintf()
+#include <stdlib.h> // for atoi() and exit()
+#include <string.h> // for memset()
+#include <unistd.h> // 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