//========================================================== // Trames.C // // Handling of the network frames, with a try to optimize the // bandwidth usage. // // Note: this class was first designed to work on an intel, // which is little endian. I made it work on big endians by // converting data to and from the network. This is okay, // but I think there is a better solution which consists in // having the sender tag the frame with the byte order used // and the receiver make the conversion if necessary. // // ZNibbles // Vincent Mallet 1997, 1998 //=========================================================== // $Id: Trame.C,v 1.6 1999/05/09 22:57:09 vmallet Exp $ /* ZNibbles - a small multiplayer game * Copyright (C) 1997, 1998 Vincent Mallet - vmallet@enst.fr * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include // #define DEBUG_TRACE // Define this to trace Frame content // #define DEBUG_LOAD // Define this to trace Frame load (cf Trame.H) #include "Trame.H" #ifdef DEBUG_LOAD unsigned long Trame::bytes_in = 0; unsigned long Trame::bytes_out = 0; unsigned long Trame::trames_in = 0; unsigned long Trame::trames_out = 0; unsigned long Trame::inf4 = 0; unsigned long Trame::inf8 = 0; unsigned long Trame::inf16 = 0; unsigned long Trame::inf20 = 0; unsigned long Trame::inf32 = 0; unsigned long Trame::inf64 = 0; unsigned long Trame::infelse = 0; #endif // DEBUG_LOAD //====================================================== // Sending and receiving //====================================================== // Send this Frame though the specified socket int Trame::send_to(int socket_number) { short len = cursize; #ifdef WORDS_BIGENDIAN rbuf[0] = (unsigned char) (len & 0xff); rbuf[1] = (unsigned char) (len >> 8); #else short& plen = * (short *) rbuf; plen = len; // hehe #endif #ifdef DEBUG_TRACE std::cout << "sending: ["; printf("%02x %02x] ", rbuf[0], rbuf[1]); for (int i = 0; i < len; i++) printf("%02x ", (unsigned int) buf[i]); std::cout << std::endl; #endif // always write at least 4 bytes write(socket_number, rbuf, (cursize <= 2) ? 4 : (cursize + 2)); #ifdef DEBUG_LOAD { int xlen = cursize + 2; if (xlen <= 4) inf4++; else if (xlen <= 8) inf8++; else if (xlen <= 16) inf16++; else if (xlen <= 20) inf20++; else if (xlen <= 32) inf32++; else if (xlen <= 64) inf64++; else infelse++; bytes_out += xlen; trames_out++; } #endif return 0; } // Fill up a Frame from the data received from // the specified socket int Trame::receive_from(int socket_number) { fd_set rfds; struct timeval tv; int retval; short len; reset(); FD_ZERO(&rfds); FD_SET(socket_number, &rfds); tv.tv_sec = timeout_s; tv.tv_usec = timeout_m; retval = select(socket_number + 1, &rfds, NULL, NULL, &tv); // @@ I should differenciate -1 and 0 if (retval <= 0) return -1; #ifdef DONT_COMPILE_THIS if (retval == 0) return -1; if (retval <= 0) { std::cerr << "trame.fuck. error=" << errno << std::endl; switch(errno) { case EBADF: std::cerr << " EBADF invalid file desc " << std::endl; break; case EINTR: std::cerr << " EINTR: signal was caught " << std::endl; break; case EINVAL: std::cerr << " EINVAL n is negative " << std::endl; break; case ENOMEM: std::cerr << " ENOMEM unable to allocate memory " << std::endl; break; } return -1; } #endif if (4 != read(socket_number, rbuf, 4)) // always read at least 4 bytes return -1; #ifdef WORDS_BIGENDIAN len = (rbuf[0] & 0xff) + ((rbuf[1] & 0xff) << 8); #else short& plen = * (short *) rbuf; len = plen; #endif if (len > 2) { int xx = read(socket_number, buf + 2, len - 2); if (xx < 0) { if (errno == 0) std::cerr << "Trame::receive_from(): error #0!" << std::endl; else std::cerr << "Trame::receive_from(): real error..." << std::endl; return -1; } else { if (xx != len - 2) { std::cerr << "Trame::receive_from(): short read (read " << xx + 2 << " instead of " << len << ")" << std::endl; return -1; } } } cursize = len; #ifdef DEBUG_TRACE std::cout << "receivd: ["; printf("%02x %02x] ", rbuf[0], rbuf[1]); for (int i = 0; i < len; i++) printf("%02x ", (int) buf[i]); std::cout << std::endl; #endif #ifdef DEBUG_LOAD { int xlen = len + 2; if (xlen <= 4) inf4++; else if (xlen <= 8) inf8++; else if (xlen <= 16) inf16++; else if (xlen <= 20) inf20++; else if (xlen <= 32) inf32++; else if (xlen <= 64) inf64++; else infelse++; bytes_in += xlen; trames_in++; } #endif return 0; } void Trame::set_timeout(long milli) { if (milli) { timeout_s = milli / 1000UL; timeout_m = (milli % 1000UL) * 1000; } else timeout_s = timeout_m = 0; } //====================================================== // Construction //====================================================== unsigned char Trame::put_byte(unsigned char val) { *(buf + cursize++) = val; return val; } schar Trame::put_char(schar val) { return *(buf + cursize++) = val; } short Trame::put_short(short val) { char *p = (char *) &val; char *q = (char *) buf + cursize; #ifdef WORDS_BIGENDIAN p++; *q++ = *p--; *q = *p; #else *q++ = *p++; *q = *p; #endif cursize += 2; return val; } int Trame::put_int(int val) { char *p = (char *) &val; char *q = (char *) buf + cursize; #ifdef WORDS_BIGENDIAN p += 3; *q++ = *p--; // faster this way than by using memcpy... *q++ = *p--; // on est oblige de faire les quatre bytes separement *q++ = *p--; // car les CPU ont du mal quand ce n'est pas aligne *q = *p; #else *q++ = *p++; // faster this way than by using memcpy... *q++ = *p++; // on est oblige de faire les quatre bytes separement *q++ = *p++; // car les CPU ont du mal quand ce n'est pas aligne *q = *p; #endif cursize += 4; return val; } long Trame::put_long(long val) { return put_int(val); } char * Trame::put_string(char *s) { put_short(strlen(s) + 1); strcpy((char *) buf + cursize, s); cursize += strlen(s)+1; return s; } //====================================================== // Access //====================================================== unsigned char Trame::peek_byte() { return (idx < cursize) ? *(unsigned char *)(buf + idx) : 0xFF; } unsigned char Trame::get_byte() { unsigned char sh = (idx < cursize) ? *(unsigned char *)(buf + idx++) : 0xFF; return sh; } //inlined, see Trame.H - char Trame::peek_char() //inlined, see Trame.H - char Trame::get_char() short Trame::peek_short() { if (idx <= cursize - 2) { short val; char *p = (char *) &val; #ifdef WORDS_BIGENDIAN *p++ = *(buf + idx + 1); *p++ = *(buf + idx + 0); #else *p++ = *(buf + idx + 0); *p++ = *(buf + idx + 1); #endif return val; } return -1; } short Trame::get_short() { short sh; if (idx <= cursize - 2) { sh = peek_short(); idx += 2; } else { std::cerr << "bordel dans get_short()" << std::endl; sh = -1; } return sh; } int Trame::peek_int() { if (idx <= cursize - 4) { int val; char *p = (char *) &val; #ifdef WORDS_BIGENDIAN *p++ = *(buf + idx + 3); *p++ = *(buf + idx + 2); *p++ = *(buf + idx + 1); *p++ = *(buf + idx + 0); #else *p++ = *(buf + idx + 0); *p++ = *(buf + idx + 1); *p++ = *(buf + idx + 2); *p++ = *(buf + idx + 3); #endif return val; } else std::cerr << "bordel dans peek_int()" << std::endl; return -1; } int Trame::get_int() { int sh; if (idx <= cursize - 4) { sh = peek_int(); idx += 4; } else { std::cerr << "bordel dans get_int()" << std::endl; sh = -1; } return sh; } long Trame::get_long() { return get_int(); } char * Trame::get_string() { int len = get_short(); if (len > 0 && idx <= cursize - len) { char *p = (char *) buf + idx; idx += len; return p; } else return NULL; } void Trame::dump_left() { std::cerr << "[ "; while (idx < cursize) std::cerr << (int) buf[idx++] << " "; std::cerr << "]" << std::endl; } //======================================================