/* * Copyright 2006 Serge van den Boom * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // This file is part of netconnection.c, from where it is #included. static inline ConnectStateData *ConnectStateData_alloc(void); static inline ConnectStateData *ConnectStateData_new(void); static inline void ConnectStateData_free(ConnectStateData *connectStateData); static void ConnectStateData_delete(ConnectStateData *connectStateData); static int NetConnection_go(NetConnection *conn); static ListenState *NetConnection_serverGo(NetConnection *conn); static ConnectState *NetConnection_clientGo(NetConnection *conn); static void NetConnection_connected(NetConnection *conn); static void NetConnection_connectedServerCallback(ListenState *listenState, NetDescriptor *listenNd, NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen); static void NetConnection_connectedClientCallback(ConnectState *connectState, NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen); static void NetConnection_connectedServerErrorCallback( ListenState *listenState, const ListenError *listenError); static void NetConnection_connectedClientErrorCallback( ConnectState *connectState, const ConnectError *connectError); //////////////////////////////////////////////////////////////////////////// static inline ConnectStateData * ConnectStateData_alloc(void) { return (ConnectStateData *) malloc(sizeof (ConnectStateData)); } static inline ConnectStateData * ConnectStateData_new(void) { ConnectStateData *connectStateData = ConnectStateData_alloc(); connectStateData->releaseFunction = (NetConnectionStateData_ReleaseFunction) ConnectStateData_delete; return connectStateData; } static inline void ConnectStateData_free(ConnectStateData *connectStateData) { free(connectStateData); } static void ConnectStateData_delete(ConnectStateData *connectStateData) { if (connectStateData->isServer) { ListenState *listenState = connectStateData->state.listenState; ListenState_close(listenState); } else { ConnectState *connectState = connectStateData->state.connectState; ConnectState_close(connectState); } ConnectStateData_free(connectStateData); } //////////////////////////////////////////////////////////////////////////// static int NetConnection_go(NetConnection *conn) { ConnectStateData *connectStateData; if (NetConnection_isConnected(conn)) return 0; if (conn->options->isServer) { ListenState *listenState; listenState = NetConnection_serverGo(conn); if (listenState == NULL) { // errno is set return -1; } connectStateData = ConnectStateData_new(); connectStateData->state.listenState = listenState; } else { ConnectState *connectState; connectState = NetConnection_clientGo(conn); if (connectState == NULL) { // errno is set return -1; } connectStateData = ConnectStateData_new(); connectStateData->state.connectState = connectState; } connectStateData->isServer = conn->options->isServer; NetConnection_setStateData(conn, (void *) connectStateData); return 0; } static ListenState * NetConnection_serverGo(NetConnection *conn) { ListenFlags listenFlags; ListenState *result; assert(conn->state == NetState_unconnected); assert(conn->options->isServer); NetConnection_setState(conn, NetState_connecting); memset (&listenFlags, 0, sizeof listenFlags); listenFlags.familyDemand = #if NETPLAY == NETPLAY_IPV4 PF_inet; #else PF_unspec; #endif listenFlags.familyPrefer = PF_unspec; listenFlags.backlog = NETPLAY_LISTEN_BACKLOG; result = listenPort(conn->options->port, IPProto_tcp, &listenFlags, NetConnection_connectedServerCallback, NetConnection_connectedServerErrorCallback, (void *) conn); return result; } static ConnectState * NetConnection_clientGo(NetConnection *conn) { ConnectFlags connectFlags; ConnectState *result; assert(conn->state == NetState_unconnected); assert(!conn->options->isServer); NetConnection_setState(conn, NetState_connecting); memset (&connectFlags, 0, sizeof connectFlags); connectFlags.familyDemand = #if NETPLAY == NETPLAY_IPV4 PF_inet; #else PF_unspec; #endif connectFlags.familyPrefer = PF_unspec; connectFlags.timeout = NETPLAY_CONNECTTIMEOUT; connectFlags.retryDelayMs = NETPLAY_RETRYDELAY; result = connectHostByName(conn->options->host, conn->options->port, IPProto_tcp, &connectFlags, NetConnection_connectedClientCallback, NetConnection_connectedClientErrorCallback, (void *) conn); return result; } // Called when an incoming connection has been established. // The caller gives up ownership of newNd static void NetConnection_connectedServerCallback(ListenState *listenState, NetDescriptor *listenNd, NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen) { NetConnection *conn = (NetConnection *) ListenState_getExtra(listenState); assert(conn->nd == NULL); conn->nd = newNd; // No incRef(); the caller gives up ownership. NetDescriptor_setExtra(conn->nd, (void *) conn); (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd)); // Ignore errors; it's not a big deal. In debug mode, a message // will already have been printed from the function itself. conn->stateFlags.discriminant = true; NetConnection_connected(conn); (void) listenNd; (void) addr; (void) addrLen; } // Called when an outgoing connection has been established. // The caller gives up ownership of newNd static void NetConnection_connectedClientCallback(ConnectState *connectState, NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen) { NetConnection *conn = (NetConnection *) ConnectState_getExtra(connectState); assert(conn->nd == NULL); conn->nd = newNd; // No incRef(); the caller gives up ownership. NetDescriptor_setExtra(conn->nd, (void *) conn); (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd)); // Ignore errors; it's not a big deal. In debug mode, a message // will already have been printed from the function itself. conn->stateFlags.discriminant = false; NetConnection_connected(conn); (void) addr; (void) addrLen; } // Called when a connection has been established. static void NetConnection_connected(NetConnection *conn) { ConnectStateData *connectStateData; conn->stateFlags.connected = true; NetConnection_setState(conn, NetState_init); connectStateData = (ConnectStateData *) NetConnection_getStateData(conn); ConnectStateData_delete(connectStateData); NetConnection_setStateData(conn, NULL); NetDescriptor_setReadCallback(conn->nd, dataReadyCallback); NetDescriptor_setCloseCallback(conn->nd, closeCallback); (*conn->connectCallback)(conn); } static void NetConnection_connectedServerErrorCallback(ListenState *listenState, const ListenError *listenError) { NetConnection *conn = (NetConnection *) ListenState_getExtra(listenState); NetConnectionError error; conn->stateFlags.connected = false; conn->stateFlags.disconnected = true; ListenState_close(listenState); NetConnection_setState(conn, NetState_unconnected); { ConnectStateData *connectStateData; connectStateData = (ConnectStateData *) NetConnection_getStateData(conn); ConnectStateData_free(connectStateData); NetConnection_setStateData(conn, NULL); } if (conn->errorCallback != NULL) { error.state = NetState_connecting; error.err = listenError->err; error.extra.listenError = listenError; //NetConnection_incRef(conn); (*conn->errorCallback)(conn, &error); //NetConnection_decRef(conn); } NetConnection_close(conn); } static void NetConnection_connectedClientErrorCallback(ConnectState *connectState, const ConnectError *connectError) { NetConnection *conn = (NetConnection *) ConnectState_getExtra(connectState); NetConnectionError error; conn->stateFlags.connected = false; conn->stateFlags.disconnected = true; ConnectState_close(connectState); NetConnection_setState(conn, NetState_unconnected); { ConnectStateData *connectStateData; connectStateData = (ConnectStateData *) NetConnection_getStateData(conn); ConnectStateData_free(connectStateData); NetConnection_setStateData(conn, NULL); } if (conn->errorCallback != NULL) { error.state = NetState_connecting; error.err = connectError->err; error.extra.connectError = connectError; //NetConnection_incRef(conn); (*conn->errorCallback)(conn, &error); //NetConnection_decRef(conn); } NetConnection_close(conn); }