This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
rc2007-soccer/source/ct-Bot/pc/tcp.c
2007-02-11 18:32:03 +00:00

282 lines
7.6 KiB
C

/*
* 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