frontlib/ipcconn.c
changeset 7162 fe76d24a25d7
parent 7160 c42949cfdd92
child 7166 8d1d6adf5f82
equal deleted inserted replaced
7160:c42949cfdd92 7162:fe76d24a25d7
     1 #include "ipcconn.h"
       
     2 #include "logging.h"
       
     3 #include "nonblocksockets.h"
       
     4 
       
     5 #include <SDL_net.h>
       
     6 #include <time.h>
       
     7 static TCPsocket ipcListenSocket;
       
     8 static NonBlockSocket ipcConnSocket;
       
     9 
       
    10 static uint8_t ipcReadBuffer[256];
       
    11 static int ipcReadBufferSize;
       
    12 
       
    13 void flib_ipcconn_init() {
       
    14 	ipcListenSocket = NULL;
       
    15 	ipcConnSocket = NULL;
       
    16 	ipcReadBufferSize = 0;
       
    17 }
       
    18 
       
    19 void flib_ipcconn_quit() {
       
    20 	flib_ipcconn_close();
       
    21 }
       
    22 
       
    23 int flib_ipcconn_listen() {
       
    24 	if(ipcListenSocket || ipcConnSocket) {
       
    25 		flib_log_e("flib_ipcconn_listen: Already listening or connected.");
       
    26 		return -1;
       
    27 	}
       
    28 	IPaddress addr;
       
    29 	addr.host = INADDR_ANY;
       
    30 
       
    31 	/* SDL_net does not seem to have a way to listen on a random unused port
       
    32 	   and find out which port that is, so let's try to find one ourselves. */
       
    33 	// TODO: Is socket binding fail-fast on all platforms?
       
    34 	srand(time(NULL));
       
    35 	rand();
       
    36 	for(int i=0; i<1000; i++) {
       
    37 		// IANA suggests using ports in the range 49152-65535 for things like this
       
    38 		int ipcPort = 49152+(rand()%(65535-49152));
       
    39 		SDLNet_Write16(ipcPort, &addr.port);
       
    40 		ipcListenSocket = SDLNet_TCP_Open(&addr);
       
    41 		if(!ipcListenSocket) {
       
    42 			flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError());
       
    43 		} else {
       
    44 			flib_log_i("Listening for IPC connections on port %i.", ipcPort);
       
    45 			return ipcPort;
       
    46 		}
       
    47 	}
       
    48 	flib_log_e("Unable to find a free port for IPC.");
       
    49 	return -1;
       
    50 }
       
    51 
       
    52 void flib_ipcconn_close() {
       
    53 	if(ipcListenSocket) {
       
    54 		SDLNet_TCP_Close(ipcListenSocket);
       
    55 		ipcListenSocket = NULL;
       
    56 	}
       
    57 	flib_nbsocket_close(&ipcConnSocket);
       
    58 	ipcReadBufferSize = 0;
       
    59 }
       
    60 
       
    61 IpcConnState flib_ipcconn_state() {
       
    62 	if(ipcConnSocket) {
       
    63 		return IPC_CONNECTED;
       
    64 	} else if(ipcListenSocket) {
       
    65 		return IPC_LISTENING;
       
    66 	} else {
       
    67 		return IPC_NOT_CONNECTED;
       
    68 	}
       
    69 }
       
    70 
       
    71 /**
       
    72  * Receive a single message and copy it into the data buffer.
       
    73  * Returns the length of the received message, -1 when nothing is received.
       
    74  */
       
    75 int flib_ipcconn_recv_message(void *data) {
       
    76 	flib_ipcconn_tick();
       
    77 
       
    78 	if(ipcConnSocket) {
       
    79 		int size = flib_nbsocket_recv(ipcConnSocket, ipcReadBuffer+ipcReadBufferSize, sizeof(ipcReadBuffer)-ipcReadBufferSize);
       
    80 		if(size>=0) {
       
    81 			ipcReadBufferSize += size;
       
    82 		} else {
       
    83 			flib_nbsocket_close(&ipcConnSocket);
       
    84 		}
       
    85 	}
       
    86 
       
    87 	int msgsize = ipcReadBuffer[0];
       
    88 	if(ipcReadBufferSize > msgsize) {
       
    89 		memcpy(data, ipcReadBuffer+1, msgsize);
       
    90 		memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1));
       
    91 		ipcReadBufferSize -= (msgsize+1);
       
    92 		return msgsize;
       
    93 	} else if(!ipcConnSocket && ipcReadBufferSize>0) {
       
    94 		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipcReadBufferSize-1, msgsize);
       
    95 		ipcReadBufferSize = 0;
       
    96 		return -1;
       
    97 	} else {
       
    98 		return -1;
       
    99 	}
       
   100 }
       
   101 
       
   102 int flib_ipcconn_send_message(void *data, size_t len) {
       
   103 	flib_ipcconn_tick();
       
   104 
       
   105 	if(!ipcConnSocket) {
       
   106 		flib_log_w("flib_ipcconn_send_message: Not connected.");
       
   107 		return -1;
       
   108 	}
       
   109 	if(len>255) {
       
   110 		flib_log_e("Attempt to send too much data to the engine in a single message.");
       
   111 		return -1;
       
   112 	}
       
   113 
       
   114 	uint8_t sendbuf[256];
       
   115 	sendbuf[0] = len;
       
   116 	memcpy(sendbuf+1, data, len);
       
   117 	if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) < len+1) {
       
   118 		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
       
   119 		flib_nbsocket_close(&ipcConnSocket);
       
   120 		return -1;
       
   121 	} else {
       
   122 		return 0;
       
   123 	}
       
   124 }
       
   125 
       
   126 void flib_ipcconn_tick() {
       
   127 	if(!ipcConnSocket && ipcListenSocket) {
       
   128 		ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true);
       
   129 		if(ipcConnSocket) {
       
   130 			SDLNet_TCP_Close(ipcListenSocket);
       
   131 			ipcListenSocket = NULL;
       
   132 		}
       
   133 	}
       
   134 }