]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
LHNET: non-blocking connected sockets
authorJānis Rūcis <parasti@gmail.com>
Mon, 29 Mar 2010 14:18:44 +0000 (17:18 +0300)
committerJānis Rūcis <parasti@gmail.com>
Sun, 27 Jun 2010 12:23:20 +0000 (15:23 +0300)
lhnet.c
lhnet.h

diff --git a/lhnet.c b/lhnet.c
index 99b9f144f941d9a3c1459c0ac781e1130c901773..517f0815fd2a371c8c6801401d666270f35118ed 100644 (file)
--- a/lhnet.c
+++ b/lhnet.c
@@ -56,6 +56,8 @@
 #if defined(WIN32)
 #define EWOULDBLOCK WSAEWOULDBLOCK
 #define ECONNREFUSED WSAECONNREFUSED
+#define EINPROGRESS WSAEINPROGRESS
+#define EALREADY WSAEALREADY
 
 #define SOCKETERRNO WSAGetLastError()
 
@@ -997,6 +999,146 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
        return NULL;
 }
 
+lhnetsocket_t *LHNET_AllocSocket(lhnetaddress_t *address)
+{
+       lhnetsocket_t *lhnetsocket;
+       if (!address)
+               return NULL;
+       lhnetsocket = (lhnetsocket_t *)Z_Malloc(sizeof (*lhnetsocket));
+       if (lhnetsocket)
+       {
+               memset(lhnetsocket, 0, sizeof (*lhnetsocket));
+               lhnetsocket->address = *address;
+               lhnetsocket->constatus = LHNETCONSTATUS_DISCONNECTED;
+               return lhnetsocket;
+       }
+       return NULL;
+}
+
+void LHNET_OpenSocket_Connected(lhnetsocket_t *lhnetsocket)
+{
+       lhnetsocket_t *s;
+       lhnetaddressnative_t *remoteaddress;
+       SOCKLEN_T namelen;
+       int connectresult;
+       if (!lhnetsocket)
+               return;
+       if (lhnetsocket->constatus == LHNETCONSTATUS_CONNECTED)
+               return;
+       switch(lhnetsocket->address.addresstype)
+       {
+               case LHNETADDRESSTYPE_LOOP:
+                       if (lhnetsocket->address.port == 0)
+                       {
+                               // allocate a port dynamically
+                               // this search will always terminate because there is never
+                               // an allocated socket with port 0, so if the number wraps it
+                               // will find the port is unused, and then refuse to use port
+                               // 0, causing an intentional failure condition
+                               lhnetsocket->address.port = 1024;
+                               for (;;)
+                               {
+                                       for (s = lhnet_socketlist.next;s != &lhnet_socketlist;s = s->next)
+                                               if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.port == lhnetsocket->address.port)
+                                                       break;
+                                       if (s == &lhnet_socketlist)
+                                               break;
+                                       lhnetsocket->address.port++;
+                               }
+                       }
+                       // check if the port is available
+                       for (s = lhnet_socketlist.next;s != &lhnet_socketlist;s = s->next)
+                               if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.port == lhnetsocket->address.port)
+                                       break;
+                       if (s == &lhnet_socketlist && lhnetsocket->address.port != 0)
+                       {
+                               lhnetsocket->next = &lhnet_socketlist;
+                               lhnetsocket->prev = lhnetsocket->next->prev;
+                               lhnetsocket->next->prev = lhnetsocket;
+                               lhnetsocket->prev->next = lhnetsocket;
+                               lhnetsocket->constatus = LHNETCONSTATUS_CONNECTED;
+                               return;
+                       }
+                       lhnetsocket->constatus = LHNETCONSTATUS_ERROR;
+                       break;
+
+               case LHNETADDRESSTYPE_INET4:
+#ifdef SUPPORTIPV6
+               case LHNETADDRESSTYPE_INET6:
+#endif
+
+#ifdef WIN32
+                       if (!lhnet_didWSAStartup)
+                       {
+                               Con_Print("LHNET_OpenSocket_Connected: can't open a socket (WSAStartup failed during LHNET_Init)\n");
+                               lhnetsocket->constatus = LHNETCONSTATUS_ERROR;
+                               return;
+                       }
+#endif
+
+                       if (lhnetsocket->constatus == LHNETCONSTATUS_DISCONNECTED)
+                       {
+#ifdef WIN32
+                               u_long _true = 1;
+#else
+                               char _true = 1;
+#endif
+
+#ifdef SUPPORTIPV6
+                               if ((lhnetsocket->inetsocket = socket(address->addresstype == LHNETADDRESSTYPE_INET6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
+#else
+                               if ((lhnetsocket->inetsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
+#endif
+                               {
+                                       Con_Printf("LHNET_OpenSocket_Connected: socket returned error: %s\n", LHNETPRIVATE_StrError());
+                                       lhnetsocket->constatus = LHNETCONSTATUS_ERROR;
+                                       break;
+                               }
+
+                               if (ioctlsocket(lhnetsocket->inetsocket, FIONBIO, &_true) == -1)
+                               {
+                                       Con_Printf("LHNET_OpenSocket_Connected: ioctlsocket returned error: %s\n", LHNETPRIVATE_StrError());
+                                       closesocket(lhnetsocket->inetsocket);
+                                       lhnetsocket->constatus = LHNETCONSTATUS_ERROR;
+                                       break;
+                               }
+                       }
+
+                       remoteaddress = (lhnetaddressnative_t *)&lhnetsocket->address;
+#ifdef SUPPORTIPV6
+                       namelen = sizeof(remoteaddress->addr.in6);
+#else
+                       namelen = sizeof(remoteaddress->addr.in);
+#endif
+                       connectresult = connect(lhnetsocket->inetsocket, &remoteaddress->addr.sock, namelen);
+
+                       if (connectresult != -1)
+                       {
+                               lhnetsocket->next = &lhnet_socketlist;
+                               lhnetsocket->prev = lhnetsocket->next->prev;
+                               lhnetsocket->next->prev = lhnetsocket;
+                               lhnetsocket->prev->next = lhnetsocket;
+                               lhnetsocket->constatus = LHNETCONSTATUS_CONNECTED;
+                       }
+                       else
+                       {
+                               if (SOCKETERRNO == EINPROGRESS || SOCKETERRNO == EALREADY)
+                               {
+                                       lhnetsocket->constatus = LHNETCONSTATUS_INPROGRESS;
+                               }
+                               else
+                               {
+                                       Con_Printf("LHNET_OpenSocket_Connected: connect returned error: %s\n", LHNETPRIVATE_StrError());
+                                       closesocket(lhnetsocket->inetsocket);
+                                       lhnetsocket->constatus = LHNETCONSTATUS_ERROR;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+       }
+}
+
 void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket)
 {
        if (lhnetsocket)
@@ -1098,6 +1240,12 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
                        }
                        Con_Printf("LHNET_Read: recvfrom returned error: %s\n", LHNETPRIVATE_StrError());
                }
+               else if (value == 0)
+               {
+                       /* Connection closed. */
+                       lhnetsocket->constatus = LHNETCONSTATUS_DISCONNECTED;
+                       return 0;
+               }
        }
 #ifdef SUPPORTIPV6
        else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET6)
@@ -1125,6 +1273,11 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
                        }
                        Con_Printf("LHNET_Read: recvfrom returned error: %s\n", LHNETPRIVATE_StrError());
                }
+               else if (value == 0)
+               {
+                       lhnetsocket->constatus = LHNETCONSTATUS_DISCONNECTED;
+                       return 0;
+               }
        }
 #endif
        return value;
diff --git a/lhnet.h b/lhnet.h
index 701045e3aa384372275c79de522ef89a0fc85ac3..b306b40bc493a97cf9dc1a4254739b05536e0930 100644 (file)
--- a/lhnet.h
+++ b/lhnet.h
@@ -30,10 +30,20 @@ int LHNETADDRESS_GetPort(const lhnetaddress_t *address);
 int LHNETADDRESS_SetPort(lhnetaddress_t *address, int port);
 int LHNETADDRESS_Compare(const lhnetaddress_t *address1, const lhnetaddress_t *address2);
 
+typedef enum lhnetconstatus_e
+{
+       LHNETCONSTATUS_DISCONNECTED,
+       LHNETCONSTATUS_INPROGRESS,
+       LHNETCONSTATUS_CONNECTED,
+       LHNETCONSTATUS_ERROR
+}
+lhnetconstatus_t;
+
 typedef struct lhnetsocket_s
 {
        lhnetaddress_t address;
        int inetsocket;
+       lhnetconstatus_t constatus;
        struct lhnetsocket_s *next, *prev;
 }
 lhnetsocket_t;
@@ -42,6 +52,8 @@ void LHNET_Init(void);
 void LHNET_Shutdown(void);
 void LHNET_SleepUntilPacket_Microseconds(int microseconds);
 lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address);
+lhnetsocket_t *LHNET_AllocSocket(lhnetaddress_t *address);
+void LHNET_OpenSocket_Connected(lhnetsocket_t *socket);
 void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket);
 lhnetaddress_t *LHNET_AddressFromSocket(lhnetsocket_t *sock);
 int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, lhnetaddress_t *address);