summaryrefslogtreecommitdiffstats
path: root/source/ct-Bot/pc/tcp.c
blob: d654f3dd07e6f72fd55bb095d619ee3892ba9285 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
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