# HG changeset patch # User Medo # Date 1338679458 -7200 # Node ID 906e72caea7b27291c723471cf2afe38b7573975 # Parent b66eef8c8092d121a07321ab44f4c1f8c0c03030 frontlib refactoring socket.h now completely wraps all the lowlevel neworking, so it would be easy to switch away from SDL_net if needed. Also reduced global state by making an IPC connection an object-like thing. diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/events.c --- a/project_files/frontlib/events.c Sat Jun 02 22:54:09 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -#include "events.h" - diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/events.h --- a/project_files/frontlib/events.h Sat Jun 02 22:54:09 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -/* - * events.h - * - * Created on: 01.06.2012 - * Author: simmax - */ - -#ifndef EVENTS_H_ -#define EVENTS_H_ - - - - -#endif /* EVENTS_H_ */ diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/frontlib.c --- a/project_files/frontlib/frontlib.c Sat Jun 02 22:54:09 2012 +0200 +++ b/project_files/frontlib/frontlib.c Sun Jun 03 01:24:18 2012 +0200 @@ -1,6 +1,6 @@ #include "frontlib.h" #include "logging.h" -#include "nonblocksockets.h" +#include "socket.h" #include "ipcconn.h" #include @@ -29,13 +29,10 @@ return -1; } - flib_ipcconn_init(); return 0; } void flib_quit() { - flib_ipcconn_quit(); - SDLNet_Quit(); if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { SDL_Quit(); @@ -43,34 +40,34 @@ } int main(int argc, char *argv[]) { - flib_init(FRONTLIB_SDL_ALREADY_INITIALIZED); - flib_ipcconn_start(true); + flib_init(0); + + flib_ipcconn ipc = flib_ipcconn_create(true, "Medo42"); char data[256]; - while(flib_ipcconn_state() != IPC_NOT_CONNECTED) { - flib_ipcconn_tick(); - int size = flib_ipcconn_recv_message(data); + while(flib_ipcconn_state(ipc) != IPC_NOT_CONNECTED) { + flib_ipcconn_tick(ipc); + int size = flib_ipcconn_recv_message(ipc, data); if(size>0) { data[size]=0; - flib_log_i("IPC IN: %s", data); switch(data[0]) { case 'C': flib_log_i("Sending config..."); - flib_ipcconn_send_messagestr("TL"); - flib_ipcconn_send_messagestr("eseed loremipsum"); - flib_ipcconn_send_messagestr("escript Missions/Training/Basic_Training_-_Bazooka.lua"); + flib_ipcconn_send_messagestr(ipc, "TL"); + flib_ipcconn_send_messagestr(ipc, "eseed loremipsum"); + flib_ipcconn_send_messagestr(ipc, "escript Missions/Training/Basic_Training_-_Bazooka.lua"); break; case '?': flib_log_i("Sending pong..."); - flib_ipcconn_send_messagestr("!"); + flib_ipcconn_send_messagestr(ipc, "!"); break; case 'Q': flib_log_i("Game interrupted."); break; case 'q': flib_log_i("Game finished."); - flib_constbuffer demobuf = flib_ipcconn_getdemo(); + flib_constbuffer demobuf = flib_ipcconn_getdemo(ipc); flib_log_i("Writing demo (%u bytes)...", demobuf.size); - FILE *file = fopen("testdemo.dem", "w"); + FILE *file = fopen("testdemo.dem", "wb"); fwrite(demobuf.data, 1, demobuf.size, file); fclose(file); file = NULL; @@ -82,6 +79,7 @@ } } flib_log_i("IPC connection lost."); + flib_ipcconn_destroy(&ipc); flib_quit(); return 0; } diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/ipcconn.c --- a/project_files/frontlib/ipcconn.c Sat Jun 02 22:54:09 2012 +0200 +++ b/project_files/frontlib/ipcconn.c Sun Jun 03 01:24:18 2012 +0200 @@ -1,97 +1,91 @@ #include "ipcconn.h" #include "logging.h" -#include "nonblocksockets.h" +#include "socket.h" -#include -#include #include #include +#include +#include -static TCPsocket ipcListenSocket; -static NonBlockSocket ipcConnSocket; +typedef struct _flib_ipcconn { + char playerName[256]; + + uint8_t readBuffer[256]; + int readBufferSize; -static uint8_t ipcReadBuffer[256]; -static int ipcReadBufferSize; + flib_acceptor acceptor; + uint16_t port; -static flib_vector demoBuffer; -static char localPlayerName[255]; + flib_tcpsocket sock; + flib_vector demoBuffer; +} _flib_ipcconn; + +flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) { + flib_ipcconn result = malloc(sizeof(_flib_ipcconn)); + flib_acceptor acceptor = flib_acceptor_create(0); -void flib_ipcconn_init() { - ipcListenSocket = NULL; - ipcConnSocket = NULL; - ipcReadBufferSize = 0; - demoBuffer=NULL; - strncpy(localPlayerName, "Local Player", 255); -} + if(!result || !acceptor) { + free(result); + flib_acceptor_close(&acceptor); + return NULL; + } + + result->acceptor = acceptor; + result->sock = NULL; + result->readBufferSize = 0; + result->port = flib_acceptor_listenport(acceptor); -void flib_ipcconn_quit() { - flib_vector_destroy(&demoBuffer); - flib_ipcconn_close(); + if(localPlayerName) { + strncpy(result->playerName, localPlayerName, 255); + } else { + strncpy(result->playerName, "Player", 255); + } + + if(recordDemo) { + result->demoBuffer = flib_vector_create(); + } + + flib_log_i("Started listening for IPC connections on port %u", result->port); + return result; } -int flib_ipcconn_start(bool recordDemo) { - if(ipcListenSocket || ipcConnSocket) { - flib_log_e("flib_ipcconn_listen: Already listening or connected."); - return -1; - } - IPaddress addr; - addr.host = INADDR_ANY; - - /* SDL_net does not seem to have a way to listen on a random unused port - and find out which port that is, so let's try to find one ourselves. */ - // TODO: Is socket binding fail-fast on all platforms? - srand(time(NULL)); - rand(); - for(int i=0; i<1000; i++) { - // IANA suggests using ports in the range 49152-65535 for things like this - int ipcPort = 49152+(rand()%(65535-49152)); - SDLNet_Write16(ipcPort, &addr.port); - ipcListenSocket = SDLNet_TCP_Open(&addr); - if(!ipcListenSocket) { - flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError()); - } else { - flib_log_i("Listening for IPC connections on port %i.", ipcPort); - if(recordDemo) { - flib_vector_destroy(&demoBuffer); - demoBuffer = flib_vector_create(); - } - return ipcPort; - } - } - flib_log_e("Unable to find a free port for IPC."); - return -1; +uint16_t flib_ipcconn_port(flib_ipcconn ipc) { + return ipc->port; } -void flib_ipcconn_close() { - if(ipcListenSocket) { - SDLNet_TCP_Close(ipcListenSocket); - ipcListenSocket = NULL; +void flib_ipcconn_destroy(flib_ipcconn *ipcptr) { + if(!ipcptr || !*ipcptr) { + return; } - flib_nbsocket_close(&ipcConnSocket); - ipcReadBufferSize = 0; + flib_ipcconn ipc = *ipcptr; + flib_acceptor_close(&ipc->acceptor); + flib_socket_close(&ipc->sock); + flib_vector_destroy(&ipc->demoBuffer); + free(ipc); + *ipcptr = NULL; } -IpcConnState flib_ipcconn_state() { - if(ipcConnSocket) { +IpcConnState flib_ipcconn_state(flib_ipcconn ipc) { + if(ipc && ipc->sock) { return IPC_CONNECTED; - } else if(ipcListenSocket) { + } else if(ipc && ipc->acceptor) { return IPC_LISTENING; } else { return IPC_NOT_CONNECTED; } } -static void demo_record(const void *data, size_t len) { - if(demoBuffer) { - if(flib_vector_append(demoBuffer, data, len) < len) { +static void demo_record(flib_ipcconn ipc, const void *data, size_t len) { + if(ipc->demoBuffer) { + if(flib_vector_append(ipc->demoBuffer, data, len) < len) { // Out of memory, fail demo recording - flib_vector_destroy(&demoBuffer); + flib_vector_destroy(&ipc->demoBuffer); } } } -static void demo_record_from_engine(const uint8_t *message) { - if(!demoBuffer || message[0]==0) { +static void demo_record_from_engine(flib_ipcconn ipc, const uint8_t *message) { + if(!ipc->demoBuffer || message[0]==0) { return; } if(strchr("?CEiQqHb", message[1])) { @@ -110,12 +104,12 @@ char converted[257]; bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4); const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s "; - int size = snprintf(converted+1, 256, template, localPlayerName, chatMsg); + int size = snprintf(converted+1, 256, template, ipc->playerName, chatMsg); converted[0] = size>255 ? 255 : size; - demo_record(converted, converted[0]+1); + demo_record(ipc, converted, converted[0]+1); } } else { - demo_record(message, message[0]+1); + demo_record(ipc, message, message[0]+1); } } @@ -123,41 +117,53 @@ * Receive a single message and copy it into the data buffer. * Returns the length of the received message, -1 when nothing is received. */ -int flib_ipcconn_recv_message(void *data) { - flib_ipcconn_tick(); +int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) { + flib_ipcconn_tick(ipc); - if(ipcConnSocket) { - int size = flib_nbsocket_recv(ipcConnSocket, ipcReadBuffer+ipcReadBufferSize, sizeof(ipcReadBuffer)-ipcReadBufferSize); + if(ipc->sock) { + int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize); if(size>=0) { - ipcReadBufferSize += size; + ipc->readBufferSize += size; } else { - flib_nbsocket_close(&ipcConnSocket); + flib_socket_close(&ipc->sock); } } - int msgsize = ipcReadBuffer[0]; - if(ipcReadBufferSize > msgsize) { - demo_record_from_engine(ipcReadBuffer); - memcpy(data, ipcReadBuffer+1, msgsize); - memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1)); - ipcReadBufferSize -= (msgsize+1); + int msgsize = ipc->readBuffer[0]; + if(ipc->readBufferSize > msgsize) { + demo_record_from_engine(ipc, ipc->readBuffer); + memcpy(data, ipc->readBuffer+1, msgsize); + memmove(ipc->readBuffer, ipc->readBuffer+msgsize+1, ipc->readBufferSize-(msgsize+1)); + ipc->readBufferSize -= (msgsize+1); return msgsize; - } else if(!ipcConnSocket && ipcReadBufferSize>0) { - flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipcReadBufferSize-1, msgsize); - ipcReadBufferSize = 0; + } else if(!ipc->sock && ipc->readBufferSize>0) { + flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize-1, msgsize); + ipc->readBufferSize = 0; return -1; } else { return -1; } } -int flib_ipcconn_send_message(void *data, size_t len) { - flib_ipcconn_tick(); +int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) { + flib_ipcconn_tick(ipc); - if(!ipcConnSocket) { + if(!ipc->sock) { flib_log_w("flib_ipcconn_send_message: Not connected."); return -1; } + + if(flib_socket_send(ipc->sock, data, len) == len) { + demo_record(ipc, data, len); + return 0; + } else { + flib_log_w("Failed or incomplete ICP write: engine connection lost."); + flib_socket_close(&ipc->sock); + return -1; + } +} + +int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) { if(len>255) { flib_log_e("Attempt to send too much data to the engine in a single message."); return -1; @@ -166,26 +172,19 @@ uint8_t sendbuf[256]; sendbuf[0] = len; memcpy(sendbuf+1, data, len); - if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) == len+1) { - demo_record(sendbuf, len+1); - return 0; - } else { - flib_log_w("Failed or incomplete ICP write: engine connection lost."); - flib_nbsocket_close(&ipcConnSocket); - return -1; - } + + return flib_ipcconn_send_raw(ipc, sendbuf, len+1); } -int flib_ipcconn_send_messagestr(char *data) { - return flib_ipcconn_send_message(data, strlen(data)); +int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) { + return flib_ipcconn_send_message(ipc, data, strlen(data)); } -void flib_ipcconn_tick() { - if(!ipcConnSocket && ipcListenSocket) { - ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true); - if(ipcConnSocket) { - SDLNet_TCP_Close(ipcListenSocket); - ipcListenSocket = NULL; +void flib_ipcconn_tick(flib_ipcconn ipc) { + if(!ipc->sock && ipc->acceptor) { + ipc->sock = flib_socket_accept(ipc->acceptor, true); + if(ipc->sock) { + flib_acceptor_close(&ipc->acceptor); } } } @@ -201,20 +200,20 @@ } } -flib_constbuffer flib_ipcconn_getdemo() { - if(!demoBuffer) { +flib_constbuffer flib_ipcconn_getdemo(flib_ipcconn ipc) { + if(!ipc->demoBuffer) { flib_constbuffer result = {NULL, 0}; return result; } - replace_gamemode(flib_vector_as_buffer(demoBuffer), 'D'); - return flib_vector_as_constbuffer(demoBuffer); + replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'D'); + return flib_vector_as_constbuffer(ipc->demoBuffer); } -flib_constbuffer flib_ipcconn_getsave() { - if(!demoBuffer) { +flib_constbuffer flib_ipcconn_getsave(flib_ipcconn ipc) { + if(!ipc->demoBuffer) { flib_constbuffer result = {NULL, 0}; return result; } - replace_gamemode(flib_vector_as_buffer(demoBuffer), 'S'); - return flib_vector_as_constbuffer(demoBuffer); + replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'S'); + return flib_vector_as_constbuffer(ipc->demoBuffer); } diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/ipcconn.h --- a/project_files/frontlib/ipcconn.h Sat Jun 02 22:54:09 2012 +0200 +++ b/project_files/frontlib/ipcconn.h Sun Jun 03 01:24:18 2012 +0200 @@ -12,42 +12,35 @@ typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState; -/** - * Called by flib_init(). Initialize everything related to ipc. - */ -void flib_ipcconn_init(); +struct _flib_ipcconn; +typedef struct _flib_ipcconn *flib_ipcconn; /** - * Called by flib_quit(). Free resources and shut down. - */ -void flib_ipcconn_quit(); - -/** - * Start listening for a connection from the engine. The system has to be in state - * IPC_NOT_CONNECTED when calling this function, and will be in state IPC_LISTENING - * if the function returns successfully. + * Start an engine connection by listening on a random port. The selected port can + * be queried with flib_ipcconn_port and has to be passed to the engine. * * The parameter "recordDemo" can be used to control whether demo recording should - * be enabled for this connection. + * be enabled for this connection. The localPlayerName is needed for demo + * recording purposes. * - * Returns the port we started listening on, or a negative value if there is an error. + * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy. * - * We stop listening once a connection has been established, so if you want to start - * the engine again and talk to it you need to call this function again after the old - * connection is closed. + * We stop accepting new connections once a connection has been established, so you + * need to create a new ipcconn in order to start a new connection. */ -int flib_ipcconn_start(bool recordDemo); +flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName); + +uint16_t flib_ipcconn_port(flib_ipcconn ipc); /** - * Close the current IPC connection and/or stop listening for an incoming one. - * This also discards all unread messages. + * Free resources, close sockets, and set the pointer to NULL. */ -void flib_ipcconn_close(); +void flib_ipcconn_destroy(flib_ipcconn *ipcptr); /** * Determine the current connection state */ -IpcConnState flib_ipcconn_state(); +IpcConnState flib_ipcconn_state(flib_ipcconn ipc); /** * Receive a single message (up to 255 bytes) and copy it into the data buffer. @@ -58,7 +51,9 @@ * no further message is returned, to ensure you see all messages that were sent * before the connection closed. */ -int flib_ipcconn_recv_message(void *data); +int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data); + +int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len); /** * Write a single message (up to 255 bytes) to the engine. This call blocks until the @@ -67,43 +62,43 @@ * Calling this function in a state other than IPC_CONNECTED will fail immediately. * Returns a negative value on failure. */ -int flib_ipcconn_send_message(void *data, size_t len); +int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len); /** * Convenience function for sending a 0-delimited string. */ -int flib_ipcconn_send_messagestr(char *data); +int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data); /** * Call regularly to allow background work to proceed */ -void flib_ipcconn_tick(); +void flib_ipcconn_tick(flib_ipcconn ipc); /** - * Get a demo record of the last connection. This should be called after + * Get a demo record of the connection. This should be called after * the connection is closed and all messages have been received. * - * If demo recording was not enabled in the last call to flib_ipcconn_start(), - * or if the recording failed for some reason, the buffer will be empty. + * If demo recording was not enabled, or if the recording failed for some reason, + * the buffer will be empty. * - * The buffer is only valid until the next call to flib_ipcconn_start() or - * a call to flib_ipcconn_getsave() (save and demo records have some minor - * differences, and those are performed directly on the buffer before returning it). + * The buffer is only valid until a call to flib_ipcconn_getsave(), since save + * and demo records have some minor differences, and those are performed directly + * on the buffer before returning it). */ -flib_constbuffer flib_ipcconn_getdemo(); +flib_constbuffer flib_ipcconn_getdemo(flib_ipcconn ipc); /** - * Get a savegame record of the last connection. This should be called after + * Get a savegame record of the connection. This should be called after * the connection is closed and all messages have been received. * - * If demo recording was not enabled in the last call to flib_ipcconn_start(), - * or if the recording failed for some reason, the buffer will be empty. + * If demo recording was not enabled, or if the recording failed for some reason, + * the buffer will be empty. * - * The buffer is only valid until the next call to flib_ipcconn_start() or - * a call to flib_ipcconn_getdemo() (save and demo records have some minor - * differences, and those are performed directly on the buffer before returning it). + * The buffer is only valid until a call to flib_ipcconn_getdemo(), since save + * and demo records have some minor differences, and those are performed directly + * on the buffer before returning it). */ -flib_constbuffer flib_ipcconn_getsave(); +flib_constbuffer flib_ipcconn_getsave(flib_ipcconn ipc); #endif /* IPCCONN_H_ */ diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/nonblocksockets.c --- a/project_files/frontlib/nonblocksockets.c Sat Jun 02 22:54:09 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -#include "nonblocksockets.h" -#include "logging.h" -#include - -static uint32_t get_peer_ip(TCPsocket sock) { - IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); - return SDLNet_Read32(&addr->host); -} - -static bool connection_is_local(TCPsocket sock) { - return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 -} - -void flib_nbsocket_close(NonBlockSocket *nbsockptr) { - NonBlockSocket nbsock = *nbsockptr; - if(nbsock!=NULL) { - SDLNet_DelSocket(nbsock->sockset, (SDLNet_GenericSocket)nbsock->sock); - SDLNet_TCP_Close(nbsock->sock); - SDLNet_FreeSocketSet(nbsock->sockset); - } - free(nbsock); - *nbsockptr = NULL; -} - -NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly) { - NonBlockSocket result = NULL; - if(!listensocket) { - flib_log_e("Attempt to accept a connection on a NULL socket."); - return NULL; - } - while(result==NULL) { - TCPsocket sock = SDLNet_TCP_Accept(listensocket); - if(!sock) { - // No incoming connections - return NULL; - } - if(localOnly && !connection_is_local(sock)) { - flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock))); - SDLNet_TCP_Close(sock); - } else { - result = malloc(sizeof(_NonBlockSocket)); - if(result==NULL) { - flib_log_e("Out of memory!"); - SDLNet_TCP_Close(sock); - return NULL; - } - result->sock = sock; - result->sockset = SDLNet_AllocSocketSet(1); - if(result->sockset==NULL) { - flib_log_e("Out of memory!"); - SDLNet_TCP_Close(sock); - free(result); - return NULL; - } - SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); - } - } - return result; -} - -int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen) { - if(!sock) { - flib_log_e("Attempt to receive on a NULL socket."); - return -1; - } - int readySockets = SDLNet_CheckSockets(sock->sockset, 0); - if(readySockets>0) { - int size = SDLNet_TCP_Recv(sock->sock, data, maxlen); - return size>0 ? size : -1; - } else if(readySockets==0) { - return 0; - } else { - flib_log_e("Error in select system call: %s", SDLNet_GetError()); - return -1; - } -} diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/nonblocksockets.h --- a/project_files/frontlib/nonblocksockets.h Sat Jun 02 22:54:09 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * nonblocksockets.h - * - * Created on: 31.05.2012 - * Author: simmax - */ - -#ifndef NONBLOCKSOCKETS_H_ -#define NONBLOCKSOCKETS_H_ - -#include -#include - -typedef struct { - TCPsocket sock; - SDLNet_SocketSet sockset; -} _NonBlockSocket; - -typedef _NonBlockSocket *NonBlockSocket; - -/** - * Close the indicated socket, free its memory and set it to NULL. - * If the socket is already NULL, nothing happens. - */ -void flib_nbsocket_close(NonBlockSocket *socket); - -/** - * Try to accept a connection from a listening socket. - * if localOnly is true, this will only accept connections which came from 127.0.0.1 - * Returns NULL if nothing can be accepted. - */ -NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly); - -/** - * Attempt to receive up to maxlen bytes from the socket, but does not - * block if nothing is available. - * Returns the ammount of data received, 0 if there was nothing to receive, - * or a negative number if the connection was closed or an error occurred. - */ -int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen); - -/** - * We can't do a nonblocking send over SDL_net, so this function just forwards - * to SDLNet_TCP_Send for convenience, which blocks until all data is sent or an - * error occurs. The ammount of data actually sent is returned, negative value on error. - */ -static inline int flib_nbsocket_blocksend(NonBlockSocket sock, void *data, int len) { - return SDLNet_TCP_Send(sock->sock, data, len); -} - -#endif /* NONBLOCKSOCKETS_H_ */ diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/socket.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/socket.c Sun Jun 03 01:24:18 2012 +0200 @@ -0,0 +1,149 @@ +#include "socket.h" +#include "logging.h" +#include +#include +#include + +typedef struct _flib_tcpsocket { + TCPsocket sock; + SDLNet_SocketSet sockset; +} _flib_tcpsocket; + +typedef struct _flib_acceptor { + TCPsocket sock; + uint16_t port; +} _flib_acceptor; + +static uint32_t get_peer_ip(TCPsocket sock) { + IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); + return SDLNet_Read32(&addr->host); +} + +static bool connection_is_local(TCPsocket sock) { + return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 +} + +flib_acceptor flib_acceptor_create(uint16_t port) { + flib_acceptor result = malloc(sizeof(_flib_acceptor)); + if(!result) { + flib_log_e("Out of memory!"); + return NULL; + } + + IPaddress addr; + addr.host = INADDR_ANY; + + if(port > 0) { + result->port = port; + SDLNet_Write16(port, &addr.port); + result->sock = SDLNet_TCP_Open(&addr); + if(result->sock) { + return result; + } else { + flib_log_e("Unable to listen on port %u: %s", port, SDLNet_GetError()); + free(result); + return NULL; + } + } else { + /* SDL_net does not seem to have a way to listen on a random unused port + and find out which port that is, so let's try to find one ourselves. */ + // TODO: Is socket binding fail-fast on all platforms? + srand(time(NULL)); + rand(); + for(int i=0; i<1000; i++) { + // IANA suggests using ports in the range 49152-65535 for things like this + result->port = 49152+(rand()%(65535-49152)); + SDLNet_Write16(result->port, &addr.port); + result->sock = SDLNet_TCP_Open(&addr); + if(result->sock) { + return result; + } else { + flib_log_i("Unable to listen on port %u: %s", result->port, SDLNet_GetError()); + } + } + flib_log_e("Unable to listen on a random unused port."); + free(result); + return NULL; + } +} + +uint16_t flib_acceptor_listenport(flib_acceptor acceptor) { + return acceptor->port; +} + +void flib_acceptor_close(flib_acceptor *acceptorptr) { + if(acceptorptr == NULL || *acceptorptr == NULL) { + return; + } + SDLNet_TCP_Close((*acceptorptr)->sock); + free(*acceptorptr); + *acceptorptr = NULL; +} + +flib_tcpsocket flib_socket_accept(flib_acceptor acceptor, bool localOnly) { + flib_tcpsocket result = NULL; + if(!acceptor) { + return NULL; + } + while(result==NULL) { + TCPsocket sock = SDLNet_TCP_Accept(acceptor->sock); + if(!sock) { + // No incoming connections + return NULL; + } + if(localOnly && !connection_is_local(sock)) { + flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock))); + SDLNet_TCP_Close(sock); + } else { + result = malloc(sizeof(_flib_tcpsocket)); + if(result==NULL) { + flib_log_e("Out of memory!"); + SDLNet_TCP_Close(sock); + return NULL; + } + result->sock = sock; + result->sockset = SDLNet_AllocSocketSet(1); + if(result->sockset==NULL) { + flib_log_e("Out of memory!"); + SDLNet_TCP_Close(sock); + free(result); + return NULL; + } + SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); + } + } + return result; +} + +void flib_socket_close(flib_tcpsocket *sockptr) { + if(sockptr==NULL || *sockptr == NULL) { + return; + } + flib_tcpsocket sock = *sockptr; + SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock); + SDLNet_TCP_Close(sock->sock); + SDLNet_FreeSocketSet(sock->sockset); + free(sock); + *sockptr = NULL; +} + +int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen) { + if(!sock) { + flib_log_e("Attempt to receive on a NULL socket."); + return -1; + } + int readySockets = SDLNet_CheckSockets(sock->sockset, 0); + if(readySockets>0) { + int size = SDLNet_TCP_Recv(sock->sock, data, maxlen); + return size>0 ? size : -1; + } else if(readySockets==0) { + return 0; + } else { + flib_log_e("Error in select system call: %s", SDLNet_GetError()); + return -1; + } +} + +int flib_socket_send(flib_tcpsocket sock, void *data, int len) { + return SDLNet_TCP_Send(sock->sock, data, len); +} diff -r b66eef8c8092 -r 906e72caea7b project_files/frontlib/socket.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/socket.h Sun Jun 03 01:24:18 2012 +0200 @@ -0,0 +1,72 @@ +/* + * Sockets for TCP networking. + * + * This layer offers some functionality over what SDL_net offers directly: listening + * sockets (called acceptors here) can be bound to port 0, which will make them listen + * on a random unused port, if one can be found. To support this feature, you can also + * query the local port that an acceptor is listening on. + * + * Further, we support nonblocking reads and writes here. The writes are buffered (TODO), + * so that all data will be accepted, and you can configure a maximum buffer size + * where the connection will be terminated. + * + * In order to ensure buffered data is actually sent out, you should regularly call + * the tick function on your sockets. + */ + +#ifndef SOCKET_H_ +#define SOCKET_H_ + +#include +#include + +struct _flib_tcpsocket; +typedef struct _flib_tcpsocket *flib_tcpsocket; + +struct _flib_acceptor; +typedef struct _flib_acceptor *flib_acceptor; + +/** + * Create a new acceptor which will listen for incoming TCP connections + * on the given port. If port is 0, this will listen on a random + * unused port which can then be queried with flib_acceptor_listenport. + * + * Can return NULL on error. + */ +flib_acceptor flib_acceptor_create(uint16_t port); + +/** + * Return the port on which the acceptor is listening. + */ +uint16_t flib_acceptor_listenport(flib_acceptor acceptor); + +/** + * Close the acceptor, free its memory and set it to NULL. + * If the acceptor is already NULL, nothing happens. + */ +void flib_acceptor_close(flib_acceptor *acceptorptr); + +/** + * Try to accept a connection from an acceptor (listening socket). + * if localOnly is true, this will only accept connections which came from 127.0.0.1 + * Returns NULL if nothing can be accepted. + */ +flib_tcpsocket flib_socket_accept(flib_acceptor acceptor, bool localOnly); + +/** + * Close the socket, free its memory and set it to NULL. + * If the socket is already NULL, nothing happens. + */ +void flib_socket_close(flib_tcpsocket *socket); + +/** + * Attempt to receive up to maxlen bytes from the socket, but does not + * block if nothing is available. + * Returns the ammount of data received, 0 if there was nothing to receive, + * or a negative number if the connection was closed or an error occurred. + */ +int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen); + +int flib_socket_send(flib_tcpsocket sock, void *data, int len); + +#endif /* SOCKET_H_ */