net.c

Go to the documentation of this file.
00001 
00024 #include "iothread.h"
00025 #include "random.h"
00026 #include "message.h"
00027 #include "timesync.h"
00028 #include "net.h"
00029 #include <string.h>
00030 #include <assert.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <SDL.h>
00034 #ifndef WIN32
00035 #include <sys/socket.h>
00036 #include <arpa/inet.h>
00037 #include <netdb.h>
00038 #include <unistd.h>
00039 #include <fcntl.h>
00040 #endif
00041 
00042 const rtsa_char *GetNetErrorString(int err) {
00043     int errInd;
00044     switch (err) {
00045         case 0:
00046             errInd = STRING_ERR_NONE;
00047             break;
00048         case EWOULDBLOCK:
00049             errInd = STRING_ERR_WOULDBLOCK;
00050             break;
00051         case EBADF:
00052             errInd = STRING_ERR_BADF;
00053             break;          
00054         case EFAULT:
00055             errInd = STRING_ERR_FAULT;
00056             break;
00057         case EINTR:
00058             errInd = STRING_ERR_INTR;
00059             break;
00060         case EINVAL:
00061             errInd = STRING_ERR_INVAL;
00062             break;
00063         case ENOTSOCK:
00064             errInd = STRING_ERR_NOTSOCK;
00065             break;
00066         case EADDRINUSE:
00067             errInd = STRING_ERR_ADDRINUSE;
00068             break;
00069         case ENETDOWN:
00070             errInd = STRING_ERR_NETDOWN;
00071             break;
00072         case ENETUNREACH:
00073             errInd = STRING_ERR_NETUNREACH;
00074             break;
00075         case ECONNRESET:
00076             errInd = STRING_ERR_CONNRESET;
00077             break;
00078         case ECONNREFUSED:
00079             errInd = STRING_ERR_CONNREFUSED;
00080             break;
00081         case EHOSTUNREACH:
00082             errInd = STRING_ERR_HOSTUNREACH;
00083             break;
00084         default:
00085             errInd = STRING_ERR_DEFAULT;
00086     }
00087     assert(strings[errInd] != NULL);
00088     return strings[errInd];
00089 }
00090 
00091 int netError = 0;
00092 
00093 ClientData *clients = NULL;
00094 
00095 unsigned int networkState = NETSTATE_SEARCHING;
00096 
00097 Uint32 clientRandId;
00098 
00103 int sock = -1;
00104 
00109 SocketAddr outgoingAddr;
00110 
00111 extern Uint32 localPlayer;
00112 
00119 static Bool ConfigNonBlocking(int sock) {
00120     #ifdef WIN32
00121     u_long val = 1;
00122     // make the change
00123     return ioctlsocket(sock, FIONBIO, &val) == 0;
00124     #else
00125     // get file options
00126     int options = fcntl(sock, F_GETFL);
00127     // check for success
00128     if (options >= 0) {
00129         // non-blocking
00130         options |= O_NONBLOCK;
00131         // set the new options
00132         return fcntl(sock, F_SETFL, options) >= 0;
00133     }
00134     return FALSE;
00135     #endif
00136 }
00137 
00146 static Bool ConfigIPv4Broadcast(Bool isServer) {
00147     int bcast = 1;
00148     assert(sock == -1);
00149     // open the socket
00150     if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
00151         // failed
00152         #ifdef USE_GETTEXT
00153         printf("Could not open an IPv4 UDP socket: %ls\n",
00154         GetNetErrorString(GetNetErrorVal()));
00155         #else
00156         printf("Could not open an IPv4 UDP socket: %s\n",
00157         GetNetErrorString(GetNetErrorVal()));
00158         #endif
00159         return FALSE;
00160     }
00161     // make it a broadcasting socket
00162     setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&bcast,
00163     sizeof(int));
00164     // setup the socket address for reception
00165     outgoingAddr.ipv4.sin_family = AF_INET;
00166     outgoingAddr.ipv4.sin_port = isServer ? htons(PORT_SRV) : htons(PORT_CLI);
00167     outgoingAddr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
00168     // bind to the port that will receive data
00169     if (bind(sock, &(outgoingAddr.addr),
00170     outgoingAddr.length = sizeof(struct sockaddr_in))) {
00171         // failed to bind
00172         #ifdef USE_GETTEXT
00173         printf("Could not bind to UDP port %u: %ls\n",
00174         ntohs(outgoingAddr.ipv4.sin_port), GetNetErrorString(GetNetErrorVal()));
00175         #else
00176         printf("Could not bind to UDP port %u: %s\n",
00177         ntohs(outgoingAddr.ipv4.sin_port), GetNetErrorString(GetNetErrorVal()));
00178         #endif
00179         return FALSE;
00180     }
00181     // setup the outgoing socket address
00182     outgoingAddr.ipv4.sin_port = isServer ? htons(PORT_CLI) : htons(PORT_SRV);
00183     outgoingAddr.ipv4.sin_addr.s_addr = htonl(INADDR_BROADCAST);
00184     // success
00185     return ConfigNonBlocking(sock);
00186 }
00187 
00188 int UnicastSocket(void *addr, Bool block) {
00189     int sock;
00190     assert(addr != NULL);
00191     // setup socket to contact the indicated system
00192     if ((sock = socket(((SocketAddr*)addr)->addr.sa_family,
00193     SOCK_DGRAM, IPPROTO_UDP)) == -1) {
00194         // failure
00195         return -1;
00196     }
00197     // bind the socket
00198     if (bind(sock, &(((SocketAddr*)addr)->addr),
00199     (((SocketAddr*)addr)->addr.sa_family == AF_INET) ?
00200     sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) {
00201         // failure
00202         CloseSocket(sock);
00203         return -1;
00204     }
00205     // use non-blocking sockets?
00206     if (!block && !ConfigNonBlocking(sock)) {
00207         // failure
00208         CloseSocket(sock);
00209         return -1;
00210     }
00211     // success
00212     return sock;
00213 }
00214 
00223 static Bool SendConnectMsg(Bool bcast) {
00224     char buff[6];
00225     // Build the message
00226     // message type
00227     buff[0] = MSG_TYPE_CONN_REQ;
00228     // broadcast flag
00229     buff[1] = bcast;
00230     // random value
00231     MSG_WRITE_32C(buff, 2, clientRandId);
00232     // send the data
00233     return sendto(sock, buff, 6, 0, (struct sockaddr*)&outgoingAddr,
00234     outgoingAddr.length) == 6;
00235 }
00236 
00237 extern char statHost[MAX_HOST_LEN];
00238 
00252 static void DiscoverConnectServer(void *srvstr) {
00253     int loop, delay = 32;
00254     // clear the string with the server's name
00255     memset(statHost, 0, MAX_HOST_LEN);
00256     // check for an input hostname
00257     if (srvstr) {
00259         // setup the socket address for the client to bind to
00260         outgoingAddr.ipv4.sin_family = AF_INET;
00261         outgoingAddr.ipv4.sin_port = htons(PORT_CLI);
00262         outgoingAddr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
00263         // set the length of the outgoing socket address
00264         outgoingAddr.length = sizeof(struct sockaddr_in);
00265         // attempt to open the socket
00266         if ((sock = UnicastSocket(&outgoingAddr, FALSE)) == -1) {
00267             // failed
00268             networkState = NETSTATE_CONERR;
00269             netError = GetNetErrorVal();
00270             return;
00271         }
00272         // try to get the address and check for validity
00273         if (inet_pton(AF_INET, srvstr, &(outgoingAddr.ipv4.sin_addr)) <= 0) {
00274             // failed; discover a server instead
00275             srvstr = NULL;
00276         }
00277         else {
00278             // set the server port for outgoing messages
00279             outgoingAddr.ipv4.sin_port = htons(PORT_SRV);
00280         }
00281     }
00282     // if discovering a server on the LAN and the socket cannot be configured
00283     if (!srvstr && !ConfigIPv4Broadcast(FALSE)) {
00284         // failed -- no way to send server discovery messages
00285         networkState = NETSTATE_SEARCHERR;
00286         netError = GetNetErrorVal();
00287         return;
00288     }
00289     // set the operating state of the networking module
00290     networkState = srvstr ? NETSTATE_CONNECTING : NETSTATE_SEARCHING;
00291     // get a random number for the client's ID
00292     clientRandId = RandNum();
00293     // make 6 attempts
00294     for (loop = 0; (loop < 24) && !IsCancelationRequested(); loop++) {
00295         // send out a connect message every fourth time through the loop
00296         if (((loop & 3) == 0) && (delay += 32,
00297         !SendConnectMsg(srvstr ? FALSE : TRUE))) {
00298             // failure
00299             networkState = srvstr ? NETSTATE_CONERR : NETSTATE_SEARCHERR;
00300             netError = GetNetErrorVal();
00301             return;
00302         }
00303         // wait for a response for at least 32ms, up to 192ms
00304         SDL_Delay(delay);
00305         //FindServer_tryAgain:
00306         if (!ReceiveMessages()) {
00307             // network trouble
00308             networkState = srvstr ? NETSTATE_CONERR : NETSTATE_SEARCHERR;
00309             netError = GetNetErrorVal();
00310             return;
00311         }
00312         // check the new network state
00313         if ((networkState == NETSTATE_RCVCFG) ||
00314         (networkState == NETSTATE_CLIENT) || (networkState == NETSTATE_SRVDENY)) {
00315             // queue up getting the hostname of the server
00316             StartNameLookup(&outgoingAddr, statHost);
00317             if (networkState == NETSTATE_RCVCFG) {
00318                 // queue up time synchronization
00319                 StartClientTimeSync();
00320             }
00321             // done -- in communication with the server
00322             return;
00323         }
00324     }
00325     // connecting to specific server?
00326     if (srvstr) {
00327         printf("Could not connect to %s; falling back on server discovery\n",
00328         (char*)srvstr);
00329         // close the socket
00330         CloseSocket(sock);
00331         // try discovery
00332         DiscoverConnectServer(NULL);
00333     }
00334     // discovery?
00335     else {
00336         // no server found, so start the process of becomming the server
00337         NetCliToSrv();
00338     }
00339 }
00340 
00341 Bool NetInit(char *srv) {
00342     // set the inital state
00343     networkState = NETSTATE_INIT;
00344     // queue up the operation to find own host information
00345     QueueOperation(DoHostnameLookup, NULL);
00346     // queue up the connection operation
00347     QueueOperation(DiscoverConnectServer, srv);
00348     // initalize the messaging system
00349     return MessageInit();
00350 }
00351 
00352 Bool NetInitSrv() {
00353     // set the inital state
00354     networkState = NETSTATE_INIT;
00355     // queue up the operation to find own host information
00356     QueueOperation(DoHostnameLookup, NULL);
00357     // try to be the server
00358     NetCliToSrv();
00359     // initalize the messaging system
00360     return MessageInit();
00361 }
00362 
00370 static void Disconnect(void *ignore) {
00371     int loop;
00372     // allow about 4 seconds for the operation
00373     for (loop = 255; loop > 0; loop--) {
00374         // don't use too much CPU time
00375         SDL_Delay(16);
00376         // attempt to process incomming messages
00377         if (!ReceiveMessages()) {
00378             // failure -- no way to continue with a reliable disconnect
00379             #ifndef NDEBUG
00380             printf("Disconnect receive error\n");
00381             #endif
00382             break;
00383         }
00384         // handle any re-transmissions
00385         ServiceMessages();
00386         // see if there are no messages left
00387         if (GetNumOutgoingMessages() == 0) {
00388             // must have received acknowledgements, so time to end
00389             break;
00390         }
00391         // see if MsgFailDropConnection() has already completed the disconnect
00392         if (networkState == NETSTATE_INIT) {
00393             return;
00394         }
00395     }
00396     // transition to the inital state
00397     if (networkState == NETSTATE_SRVDISCONN) {
00398         NetSrvToCli();
00399     }
00400     else {
00401         CloseSocket(sock);
00402         networkState = NETSTATE_INIT;
00403         timeSyncState = TIMESYNC_IDLE;
00404     }
00405     #ifndef NDEBUG
00406     printf("Disconnect complete\n");
00407     #endif
00408 }
00409 
00410 void NetUninit() {
00411     // assure that the time sync operation is no longer running
00412     StopServerTimeSync();
00413     // if the process has not yet started to disconnect . . .
00414     if ((networkState == NETSTATE_RCVCFG) ||
00415     (networkState == NETSTATE_CLIENT) || (networkState == NETSTATE_SERVER)) {
00416         // remove all existing messages from the system
00417         CancelAllMessages();
00418         // inform remote host(s) of disconnection
00419         SendDisconnectMessage(localPlayer);
00420         // change the network state to reflect this
00421         networkState++;
00422         // disconnect from remote host(s)
00423         Disconnect(NULL);
00424     }
00425     // if the process is working on the disconnect . . .
00426     else if ((networkState == NETSTATE_CLIDISCONN) ||
00427     (networkState == NETSTATE_SRVDISCONN)) {
00428         // wait for the operation to complete
00429         /*
00430         do {
00431             SDL_Delay(25);
00432         } while (networkState != NETSTATE_INIT);
00433         */
00434         Disconnect(NULL);
00435     }
00436     // uninitialize the messaging system
00437     MessageUninit();
00438     // close the socket
00439     CloseSocket(sock);
00440     // if server-side client data was stored . . .
00441     if (clients) {
00442         // . . . get rid of it
00443         free(clients);
00444         clients = NULL;
00445     }
00446 }
00447 
00448 void NetDisconnect() {
00449     // assure that the time sync operation is no longer running
00450     StopServerTimeSync();
00451     // assure a disconnect occurs only when connected
00452     if ((networkState == NETSTATE_RCVCFG)
00453     || (networkState == NETSTATE_CLIENT) || (networkState == NETSTATE_SERVER)) {
00454         // try to clear the I/O thread
00455         CancelOperations();
00456         // remove all existing messages from the system
00457         CancelAllMessages();
00458         // inform remote host(s) of disconnection
00459         SendDisconnectMessage(localPlayer);
00460         // change the network state to reflect this
00461         networkState++;
00462         // handle the disconnect in the I/O thread
00463         QueueOperation(Disconnect, NULL);
00464     }
00465 }
00466 
00467 // Prototypes for externally defined functions that aren't called anywhere else
00468 Bool MessageCliToSrv();
00469 Bool MessageSrvToCli();
00470 
00471 Bool NetCliToSrv() {
00472     // allocate memory for server specific data on each client
00473     if ((clients = calloc(MAX_PLAYERS, sizeof(ClientData))) == NULL) {
00474         // allocation failed!
00475         networkState = NETSTATE_SRVERR;
00476         netError = 0;
00477         return FALSE;
00478     }
00479     // close the socket
00480     CloseSocket(sock);
00481     // open the socket bound to a different port
00482     if (!ConfigIPv4Broadcast(TRUE) || !StartServerTimeSync()) {
00483         // no socket, no server
00484         free(clients);
00485         clients = NULL;
00486         networkState = NETSTATE_SRVERR;
00487         netError = GetNetErrorVal();
00488         return FALSE;
00489     }
00490     // setup the local client
00494     localPlayer = 0;
00495     clients->contact = clients->active = TRUE;
00496     // change the message system state
00497     if (MessageCliToSrv()) {
00498         // set the server state
00499         networkState = NETSTATE_SERVER;
00500         // sucess
00501         return TRUE;
00502     }
00503     StopServerTimeSync();
00504     // failure
00505     return FALSE;
00506 }
00507 
00508 Bool NetSrvToCli() {
00509     Bool result;
00510     assert(networkState == NETSTATE_SRVDISCONN);
00511     // server specific data on clients is not needed by a client, so
00512     // deallocate it
00513     free(clients);
00514     clients = NULL;
00515     // close the socket
00516     CloseSocket(sock);
00517     // the socket will need to be reopened
00518     // change the message system state
00519     result = MessageSrvToCli();
00520     // kill the time sync thread
00521     StopServerTimeSync();
00522     // set the new state
00523     networkState = NETSTATE_INIT;
00524     return result;
00525 }
00526 

Generated on Mon May 28 04:41:39 2007 for Retro Tank Super Attack by  doxygen 1.5.2