/* * net_tele.c - telegrams for cleint/server communication * * $Id: net_tele.c,v 1.6 2004/06/04 11:24:19 iskywalker Exp $ * * Program XBLAST * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net) * * 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; or (at your option) * any later version * * This program is distributed in the hope that it will be entertaining, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILTY 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 */ #include "net_tele.h" /* * local macros */ #define MAX_HEADER_SIZE 5 #define MAX_DATA_SIZE 255 #define MAX_TAIL_SIZE 1 #define MAX_TOTAL_SIZE (MAX_HEADER_SIZE + MAX_DATA_SIZE + MAX_TAIL_SIZE) #define START_BYTE 0x68 #define STOP_BYTE 0x16 /* * local types */ typedef int (*WriteFunc) (const XBSocket *, const void *, size_t); typedef int (*ReadFunc) (const XBSocket *, void *, size_t); typedef enum { PS_Start, PS_Len, PS_COT, PS_ID, PS_IOB, PS_Data, PS_Stop } ParseState; struct _xb_telegram { XBTeleCOT cot; XBTeleID id; unsigned char iob; void *data; size_t len; XBTelegram *next; }; typedef struct _xb_any_queue { XBTelegram *first; XBTelegram *last; } XBAnyQueue; struct _xb_snd_queue { XBAnyQueue any; size_t wIndex; size_t wLen; char wBuf[MAX_TOTAL_SIZE]; }; struct _xb_rcv_queue { XBAnyQueue any; size_t rIndex; size_t rLen; XBBool rFlag; ParseState rState; size_t dCount; char rBuf[MAX_TOTAL_SIZE]; char tBuf[MAX_TOTAL_SIZE]; }; /*------------------------------------------------------------------------* * * Debugoutput * *------------------------------------------------------------------------*/ #ifdef DEBUG_TELE const char * StringCOT (XBTeleCOT cot) { switch (cot) { case XBT_COT_Activate: return "act"; case XBT_COT_Spontaneous: return "spont"; case XBT_COT_SendData: return "snd_dat"; case XBT_COT_RequestData: return "req_dat"; case XBT_COT_DataNotAvailable: return "dat_not"; case XBT_COT_DataAvailable: return "dat_ava"; default: return "???"; } } /* StringID */ const char * StringID (XBTeleID id) { switch (id) { case XBT_ID_GameConfig: return "gam_cfg"; case XBT_ID_PlayerConfig: return "plr_cfg"; case XBT_ID_RequestDisconnect: return "req_dis"; case XBT_ID_HostDisconnected: return "hst_dis"; case XBT_ID_StartGame: return "sta_gam"; case XBT_ID_RandomSeed: return "rnd_sed"; case XBT_ID_LevelConfig: return "lvl_cfg"; case XBT_ID_DgramPort: return "dgm_prt"; case XBT_ID_Sync: return "sync"; case XBT_ID_HostIsIn: return "hst_in"; case XBT_ID_HostIsOut: return "hst_out"; case XBT_ID_TeamChange: return "tm_cng"; default: return "???"; } } /* StringID */ /* * debugouput */ static void DebugTelegram (FILE *fout, const XBTelegram *tele) { fprintf (fout, "%s %s %03u (%03u Bytes)", StringID (tele->id), StringCOT (tele->cot), (unsigned) tele->iob, (unsigned) tele->len); if (tele->len > 0) { size_t i; const char *s = tele->data; fputs (" \"", fout); for (i = 0; i < tele->len; i ++) { if (isprint (s[i])) { fputc (s[i], fout); } else { fprintf (fout, "\\%03o", (unsigned) s[i]); } } fputc ('\"', fout); } } /* DebugTelegram */ #endif /*------------------------------------------------------------------------* * * XBTelegram * *------------------------------------------------------------------------*/ /* * create a new telgramm */ XBTelegram * Net_CreateTelegram (XBTeleCOT cot, XBTeleID id, XBTeleIOB iob, const void *buf, size_t len) { XBTelegram *tele; assert (len < MAX_DATA_SIZE); tele = calloc (1, sizeof (XBTelegram)); assert (tele != NULL); tele->cot = cot; tele->id = id; tele->iob = iob; /* copy buffer if needed */ if (buf != NULL && len != 0) { tele->len = len; tele->data = malloc (len); memcpy (tele->data, buf, len); } return tele; } /* CreateTelegram */ /* * delete a telegram */ void Net_DeleteTelegram (XBTelegram *tele) { assert (tele != NULL); if (NULL != tele->data) { free (tele->data); } free (tele); } /* Net_DeleteTelegram */ /* * write telegram to buffer */ static size_t WriteTelegram (const XBTelegram *tele, char *buf) { char *ptr = buf; /* write header */ *ptr ++ = START_BYTE; *ptr ++ = tele->len; *ptr ++ = (char) tele->cot; *ptr ++ = (char) tele->id; *ptr ++ = (char) tele->iob; /* write data */ assert (tele->len < MAX_DATA_SIZE); if (tele->len > 0) { assert (tele->data != NULL); memcpy (ptr, tele->data, tele->len); ptr += tele->len; } /* write tail */ *ptr ++ = STOP_BYTE; /* return len */ return ptr - buf; } /* WriteTelegram */ /*------------------------------------------------------------------------* * * XBAnyQueue * *------------------------------------------------------------------------*/ static void DeleteAnyQueue (XBAnyQueue *list) { XBTelegram *teleNext; for ( ; list->first != NULL; list->first = teleNext) { teleNext = list->first->next; Net_DeleteTelegram (list->first); } } /* DeleteAnyQueue */ /* * add telegram to list */ static void AddTelegram (XBAnyQueue *list, XBTelegram *tele) { assert (list != NULL); assert (tele != NULL); if (NULL == list->last) { list->first = tele; } else { list->last->next = tele; } list->last = tele; #ifdef DEBUG_TELE fputs ("add tele:", stderr); DebugTelegram (stderr, tele); fputc ('\n', stderr); #endif } /* Net_AddTelegram */ /* * get first telegram from list */ static XBTelegram * GetTelegram (XBAnyQueue *list) { XBTelegram *tele; assert (list != NULL); /* first element from list */ tele = list->first; /* update list if needed */ if (list->first != NULL) { list->first = list->first->next; } if (list->first == NULL) { list->last = NULL; } return tele; } /* GetTelegram */ /*------------------------------------------------------------------------* * * XBSndQueue * *------------------------------------------------------------------------*/ /* * create write queue */ XBSndQueue * Net_CreateSndQueue (XBBool server) { XBSndQueue *list = calloc (1, sizeof (*list)); assert (list != NULL); return list; } /* Net_CreateSndQueue */ /* * delete telegram list */ void Net_DeleteSndQueue (XBSndQueue *list) { DeleteAnyQueue (&list->any); free (list); } /* Net_DeleteSndQueue */ /* * socket has become writeable */ XBTeleResult Net_Send (XBSndQueue *list, const XBSocket *pSocket) { int result; XBTelegram *tele; assert (list != NULL); /* check if new telegram must written to buffer */ if (0 == list->wLen && NULL != (tele = GetTelegram (&list->any) ) ) { #ifdef DEBUG_TELE Dbg_Out ("wrt tele %d (%03u bytes)\n", Socket_Fd (pSocket), tele->len); #endif list->wLen = WriteTelegram (tele, list->wBuf); list->wIndex = 0; Net_DeleteTelegram (tele); } /* check if any unwritten bytes are left in the buffer */ if (list->wIndex < list->wLen) { result = Socket_Send (pSocket, list->wBuf + list->wIndex, list->wLen - list->wIndex); switch (result) { case XB_SOCKET_ERROR: Dbg_Out ("ERROR while writing\n"); return XBT_R_IOError; case XB_SOCKET_END_OF_FILE: Dbg_Out ("END_OF_FILE while writing\n"); return XBT_R_IOError; case XB_SOCKET_WOULD_BLOCK: return XBT_R_Continue; default: break; } list->wIndex += result; if (list->wIndex == list->wLen) { list->wLen = list->wIndex = 0; } } /* check if we need to call write again */ if (list->wLen > 0 || list->any.first != NULL) { return XBT_R_Continue; } else { return XBT_R_Complete; } } /* Net_TeleWrite */ /* * queue telegramm for sending */ void Net_SendTelegram (XBSndQueue *list, XBTelegram *tele) { #ifdef DEBUG_TELE Dbg_Out (" > snd "); #endif AddTelegram (&list->any, tele); } /* Net_SendTelegram */ /*------------------------------------------------------------------------* * * XBRcvQueue * *------------------------------------------------------------------------*/ /* * create telegram list */ XBRcvQueue * Net_CreateRcvQueue (XBBool server) { XBRcvQueue *list = calloc (1, sizeof (*list)); assert (list != NULL); list->rState = PS_Start; list->rFlag = XBTrue; return list; } /* Net_CreateSndQueue */ /* * delete telegram list */ void Net_DeleteRcvQueue (XBRcvQueue *list) { DeleteAnyQueue (&list->any); free (list); } /* Net_DeleteSndQueue */ /* * read buffer from socket */ static XBTeleResult ReadBuffer (XBRcvQueue *list, const XBSocket *pSocket) { #ifdef W32 long result; #else ssize_t result; #endif result = Socket_Receive (pSocket, list->rBuf, MAX_TOTAL_SIZE); switch (result) { case XB_SOCKET_ERROR: return XBT_R_IOError; case XB_SOCKET_END_OF_FILE: return XBT_R_EndOfFile; case XB_SOCKET_WOULD_BLOCK: result = 0; break; } list->rLen = result; list->rIndex = 0; return XBT_R_Continue; } /* ReadBuffer */ /* * fetch byte from internal read buffer */ static XBBool GetByte (XBRcvQueue *list, size_t tIndex) { if (list->rIndex < list->rLen) { list->tBuf[tIndex] = list->rBuf[list->rIndex ++]; return XBTrue; } else { return XBFalse; } } /* GetByte */ /* * socket has become readable */ XBTeleResult Net_Receive (XBRcvQueue *list, const XBSocket *pSocket) { size_t i, len; XBTelegram *tele; assert (list != NULL); /* do we need to read bytes */ if (list->rFlag) { XBTeleResult result; if (XBT_R_Continue != (result = ReadBuffer (list, pSocket) ) ) { return result; } list->rFlag = XBFalse; } /* look for start byte */ while (1) { switch (list->rState) { /* * read start byte */ case PS_Start: do { if (! GetByte (list, 0)) { list->rFlag = XBTrue; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('{', stderr); #endif } while (list->tBuf[0] != START_BYTE); list->rState = PS_Len; break; /* * read len byte */ case PS_Len: if (! GetByte (list, 1)) { list->rFlag = XBTrue; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('L', stderr); #endif list->rState = PS_COT; break; /* * read cause of transmission */ case PS_COT: if (! GetByte (list, 2)) { list->rFlag = XBTrue; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('C', stderr); #endif list->rState = PS_ID; break; /* * read telegramm id */ case PS_ID: if (! GetByte (list, 3)) { list->rFlag = XBTrue; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('I', stderr); #endif list->rState = PS_IOB; break; /* * read telegram info object address */ case PS_IOB: if (! GetByte (list, 4) ) { list->rFlag = XBTrue; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('i', stderr); #endif list->rState = PS_Data; list->dCount = 0; break; /* * read telegram data */ case PS_Data: len = list->tBuf[1]; for (i = list->dCount; i < len; i ++) { if (! GetByte (list, MAX_HEADER_SIZE + i)) { list->rFlag = XBTrue; list->dCount = i; return XBT_R_Continue; } #ifdef DEBUG_TELE fputc ('.', stderr); #endif } #ifdef DEBUG_TELE fputc ('D', stderr); #endif list->rState = PS_Stop; break; /* * check stop byte */ case PS_Stop: len = list->tBuf[1]; if (! GetByte (list, MAX_HEADER_SIZE + len)) { list->rFlag = XBTrue; return XBT_R_Continue; } if (STOP_BYTE != list->tBuf[MAX_HEADER_SIZE + len]) { list->rState = PS_Start; return XBT_R_TeleError; } #ifdef DEBUG_TELE Dbg_Out ("}\n"); #endif /* telegram is now complete */ tele = Net_CreateTelegram (list->tBuf[2], list->tBuf[3], list->tBuf[4], list->tBuf + MAX_HEADER_SIZE, len); assert (NULL != tele); #ifdef DEBUG_TELE Dbg_Out (" < rcv "); #endif AddTelegram (&list->any, tele); /* start from the beginning */ list->rState = PS_Start; } } return XBT_R_TeleError; } /* Net_ReadTelegram */ /* * get first telegram in receive queue */ XBTelegram * Net_ReceiveTelegram (XBRcvQueue *list) { return GetTelegram (&list->any); } /* Net_ReceiveTelegram */ /* * get caus of telegram transmission */ XBTeleCOT Net_TeleCOT (const XBTelegram *tele) { assert (tele != NULL); return tele->cot; } /* Net_TeleCOT */ /* * get id of telegram */ XBTeleID Net_TeleID (const XBTelegram *tele) { assert (tele != NULL); return tele->id; } /* Net_TeleID */ /* * get iob of telegram */ XBTeleIOB Net_TeleIOB (const XBTelegram *tele) { assert (tele != NULL); return tele->iob; } /* Net_TeleIOB */ /* * get telegram data */ const void * Net_TeleData (const XBTelegram *tele, size_t *len) { assert (tele != NULL); assert (len != NULL); *len = tele->len; return tele->data; } /* Net_TeleData */ /* * end of file net_tele.c */