ecere/drivers/Android: Tweaks to run with latest Android SDK/NDK on Nexus 9
[sdk] / ecere / src / net / Socket.ec
1 namespace net;
2
3 #include <stdarg.h>
4
5 #if defined(__WIN32__)
6
7 #define WIN32_LEAN_AND_MEAN
8 #define String _String
9 #include <winsock.h>
10 #undef String
11 #define SOCKLEN_TYPE int
12
13 #elif defined(__unix__) || defined(__APPLE__)
14
15 default:
16 #define SOCKLEN_TYPE socklen_t
17 #define set _set
18 #define uint _uint
19 #include <sys/time.h>
20 #include <unistd.h>
21
22 #include <netinet/in.h>
23 #include <netdb.h>
24 #include <sys/socket.h>
25 #include <sys/wait.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <arpa/inet.h>
29 #undef set
30 #undef uint
31 private:
32
33 typedef int SOCKET;
34 typedef struct hostent HOSTENT;
35 typedef struct sockaddr SOCKADDR;
36 typedef struct sockaddr_in SOCKADDR_IN;
37 typedef struct in_addr IN_ADDR;
38 #define closesocket(s) close(s)
39
40 #endif
41
42 import "network"
43
44 #ifdef __ANDROID__
45 // TOFIX:
46 #undef ntohs
47 #undef htons
48 #define ntohs(x) (x)
49 #define htons(x) (x)
50 #endif
51
52 #define GETLEDWORD(b) (uint32)(((b)[3] << 24) | ((b)[2] << 16) | ((b)[1] << 8) | (b)[0])
53
54 #define PUTLEDWORD(b, d) \
55    (b)[3] = (byte)(((d) >> 24) & 0xFF); \
56    (b)[2] = (byte)(((d) >> 16) & 0xFF); \
57    (b)[1] = (byte)(((d) >> 8)  & 0xFF); \
58    (b)[0] = (byte)( (d)        & 0xFF);
59
60 public enum SocketType { tcp, udp };
61 public enum DisconnectCode { remoteLost = 1, remoteClosed = 2, resolveFailed = 3, connectFailed = 4 };
62
63 public class Packet : struct
64 {
65 public:
66    uint size;
67 };
68
69 static class SocketConnectThread : Thread
70 {
71    Socket socket;
72
73    uint Main()
74    {
75       bool result = false;
76       HOSTENT * host = gethostbyname(socket.address);
77       if(host)
78       {
79          network.mutex.Wait();
80
81          if(!socket.destroyed)
82          {
83             socket.a.sin_addr = *((IN_ADDR *)host->h_addr);
84             network.mutex.Release();
85             if(socket.type == udp ||
86                !connect(socket.s,(SOCKADDR *)&socket.a,sizeof(socket.a)))
87             {
88                network.mutex.Wait();
89                strcpy(socket.inetAddress, inet_ntoa(socket.a.sin_addr));
90                socket.inetPort = ntohs(socket.a.sin_port);
91                network.mutex.Release();
92
93                if(socket.OnEstablishConnection((int)socket.s))
94                {
95                   network.mutex.Wait();
96                   result = true;
97                }
98                else
99                {
100                   network.mutex.Wait();
101                   socket.disconnectCode = connectFailed;
102                }
103             }
104             else
105             {
106                network.mutex.Wait();
107                socket.disconnectCode = connectFailed;
108             }
109          }
110       }
111       else
112          socket.disconnectCode = resolveFailed;
113
114    #ifdef DEBUG_SOCKETS
115       Log("[C] Signaling connect event (%X)\n", socket);
116    #endif
117       if(result && !socket.destroyed)
118          socket._connected = 1;
119       else if(socket._connected == -2)
120          socket._connected = -1;
121
122    #ifdef DEBUG_SOCKETS
123       Log("[C] Getting out of connect thread (%X)\n", socket);
124    #endif
125       network.connectEvent = true;
126       if(guiApp)
127          guiApp.SignalEvent();
128       network.mutex.Release();
129       return 0;
130    }
131
132 };
133
134 public class Socket
135 {
136 public:
137    property Service service
138    {
139       set
140       {
141          if(value)
142          {
143             SOCKET s;
144             SOCKADDR_IN a;
145             SOCKLEN_TYPE addrLen = sizeof(a);
146
147             value.accepted = true;
148             s = accept(value.s,(SOCKADDR *)&a, &addrLen);
149             if(s != -1)
150             {
151                int sendsize=65536;
152                int recvsize=65536;
153
154                value.sockets.Add(this);
155                incref this;
156
157                setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&sendsize, (int)sizeof(sendsize));
158                setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize, (int)sizeof(recvsize));
159
160                destroyed = false;
161                // TESTING THIS HERE
162                _connected = 1;
163                address = null;
164                this.a = a;
165                strcpy(inetAddress, inet_ntoa(this.a.sin_addr));
166                inetPort = ntohs(a.sin_port);
167                this.s = s;
168                service = value;
169                connectThread = null;
170                disconnectCode = (DisconnectCode)-1;
171                disconnected = false;
172
173                network.mutex.Wait();
174                FD_SET(s, &network.exceptSet);
175                FD_SET(s, &network.readSet);
176                if(s >= network.ns)
177                {
178                   network.ns = (int)(s+1);
179                   network.socketsSemaphore.Release();
180                }
181                network.mutex.Release();
182             }
183             else if(!_refCount)
184             {
185                delete this;
186             }
187          }
188       }
189       get { return this ? service : null; }
190    };
191
192    property const char * inetAddress { get { return (char *)inetAddress; } };
193    property int inetPort { get { return inetPort; } }
194    property Socket next { get { return next; } };
195    property bool connected { get { return _connected == 1 || _connected == -2; } };
196    property bool processAlone { get { return processAlone; } set { processAlone = value; } };
197
198    virtual void OnConnect(void);
199    virtual uint OnReceive(const byte * buffer, uint count)
200    {
201       if(count >= sizeof(class Packet))
202       {
203          Packet packet = (Packet)buffer;
204          uint size = GETLEDWORD((byte *)&packet.size);
205          if(count >= size)
206          {
207             byte * tempBuffer = null;
208             if(size)
209             {
210                if(recvBytes - size)
211                {
212                   tempBuffer = new byte[size];
213                   packet = (Packet)tempBuffer;
214                   memcpy(tempBuffer, buffer, size);
215                   memmove(recvBuffer, recvBuffer + size, recvBytes - size);
216                }
217                recvBytes -= size;
218             }
219             OnReceivePacket(packet);
220             delete tempBuffer;
221             return 0;
222          }
223       }
224       return 0;
225    }
226
227    virtual void OnDisconnect(int code);
228    virtual void OnReceivePacket(Packet packet);
229
230    bool Connect(const char * address, int port)
231    {
232       bool result = false;
233    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
234       SOCKET s = socket(AF_INET,SOCK_STREAM,0);
235    #ifdef DEBUG_SOCKETS
236       Log("\n[P] [NConnect]\n");
237    #endif
238       if(s != -1)
239       {
240          result = _Connect(s, address, port);
241       }
242    #endif
243       return result;
244    }
245
246    void Disconnect(DisconnectCode code)
247    {
248       if(this)
249       {
250          bool wasDisconnected = disconnected;
251    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
252          SOCKET s = this.s;
253
254    #ifdef DEBUG_SOCKETS
255          Logf("[P] [NDisconnect (%X, %x)]\n", this, this.s);
256    #endif
257          network.mutex.Wait();
258          disconnectCode = code;
259          destroyed = true;
260
261          if(!disconnected)
262          {
263             if(_connected == -2 && connectThread)
264             {
265                incref this;
266                network.mutex.Release();
267                connectThread.Wait();
268                delete connectThread;
269                network.mutex.Wait();
270                _refCount--;
271             }
272             disconnected = true;
273             if(!service)
274             {
275                if(_connected)
276                   ((_connected == -1 || _connected == -2) ? network.connectSockets : network.sockets).Remove(this);
277             }
278             else
279             {
280                service.sockets.Remove(this);
281                service = null;
282             }
283             _connected = 0;
284             network.mutex.Release();
285             OnDisconnect(disconnectCode);
286             network.mutex.Wait();
287          }
288
289          if(s == network.ns - 1)
290             Network_DetermineMaxSocket();
291
292          if(s != -1)
293          {
294             FD_CLR(s, &network.readSet);
295             FD_CLR(s, &network.writeSet);
296             FD_CLR(s, &network.exceptSet);
297          }
298          // Why wasn't this here? Don't want it here :) Hmm why don't we want it here? Service created socket not getting freed in DICOMTest...
299          // Trying >= 1 instead of > 1
300          // Free(code);
301          //if(_refCount > 1)
302          /*if(_refCount >= 1)
303             _refCount--;*/
304
305          shutdown(s, 2);
306
307          if(!wasDisconnected)
308             delete this;
309
310          network.mutex.Release();
311    #endif
312       }
313    }
314
315    // --- Transfer ---
316    bool Send(const void * buffer, int size)
317    {
318    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
319       if(this)
320       {
321          SOCKET s = this.s;
322          int count;
323          fd_set ws, es;
324
325          if(s != -1 && ((type == tcp && (count = SendData(buffer, size, 0))) ||
326             (type == udp && (count = (int)sendto(s, buffer, size,0, (SOCKADDR *)&a, sizeof(a))))))
327          {
328    #if defined(__WIN32__)
329             int error = WSAGetLastError();
330    #endif
331             FD_ZERO(&ws);
332             FD_ZERO(&es);
333             FD_SET(s, &ws);
334             FD_SET(s, &es);
335    #if defined(__WIN32__)
336             if(error)
337    #endif
338             {
339                //Print("~");
340             }
341
342             // This is what was making eCom jam...
343             // select(s+1, null, &ws, &es, null);
344             return true;
345          }
346       }
347    #endif
348       return false;
349    }
350
351    bool SendPacket(Packet packet)
352    {
353       if(this && packet)
354       {
355          bool result;
356          uint size = packet.size;
357          PUTLEDWORD((byte *)&packet.size, size);
358          result = Send(packet, size);
359          packet.size = size;
360          return result;
361       }
362       return false;
363    }
364
365    bool SendString(const char * string)
366    {
367       return Send(string, (int)strlen(string));
368    }
369
370    bool Sendf(const char * format, ...)
371    {
372       bool result;
373       va_list args;
374       char string[MAX_F_STRING];
375       va_start(args, format);
376       vsnprintf(string, sizeof(string), format, args);
377       string[sizeof(string)-1] = 0;
378       result = Send(string, (int)strlen(string));
379       va_end(args);
380       return result;
381    }
382
383    bool DatagramConnect(const char * sendAddress, int port)
384    {
385       SOCKET s = socket(AF_INET,SOCK_DGRAM,0);
386       if(s != -1)
387       {
388          _Connect(s, sendAddress, port);
389          type = udp;
390          return true;
391       }
392       return false;
393    }
394
395    bool DatagramHost(int port)
396    {
397       SOCKET s = socket(AF_INET,SOCK_DGRAM,0);
398       if(s != -1 && !_connected)
399       {
400          SOCKADDR_IN a;
401          bool val = true;
402          a.sin_family=AF_INET;
403          a.sin_port = htons((uint16)port);
404          a.sin_addr.s_addr=INADDR_ANY;
405          setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
406          if(!bind(s,(SOCKADDR *)&a, sizeof(a)))
407          {
408             network.mutex.Wait();
409             type = udp;
410             this.s = s;
411             network.sockets.Add(this);
412             incref this;
413
414             disconnectCode = (DisconnectCode)-1;
415             _connected = 1;
416
417             FD_CLR(s, &network.writeSet);
418             FD_SET(s, &network.readSet);
419             FD_SET(s, &network.exceptSet);
420             if(s >= network.ns)
421             {
422                network.ns = (int)(s+1);
423                network.socketsSemaphore.Release();
424             }
425             network.mutex.Release();
426             return true;
427          }
428          closesocket(s);
429       }
430       return false;
431    }
432
433    virtual int ReceiveData(byte * buffer, int count, uint flags)
434    {
435       return (int)recv(s, (char *)buffer, count, flags);
436    }
437    virtual int SendData(const byte * buffer, int count, uint flags)
438    {
439       return (int)send(s, (const char *)buffer, count, flags);
440    }
441    virtual bool OnEstablishConnection(int s);
442
443 private:
444    Socket()
445    {
446       Network_Initialize();
447       disconnected = true;
448    }
449
450    ~Socket()
451    {
452       _refCount = MAXINT;
453       Free(true);
454       _refCount = 0;
455    }
456
457 #ifndef ECERE_NONET
458    void Free(bool mustLock)
459    {
460       SOCKET s = this.s;
461
462       if(mustLock) network.mutex.Wait();
463
464       if(!service && _connected)
465       {
466          ((_connected == -1 || _connected == -2) ? network.connectSockets : network.sockets).Remove(this);
467          _connected = 0;
468       }
469
470       if(!disconnected)
471       {
472          disconnected = true;
473          network.mutex.Release();
474          incref this;
475          OnDisconnect(disconnectCode);
476          // if(_refCount > 1) _refCount--;
477          _refCount--;
478          network.mutex.Wait();
479       }
480
481       if(service)
482       {
483          service.sockets.Remove(this);
484          service = null;
485          _connected = 0;
486       }
487
488       if(s != -1) { closesocket(s); this.s = -1; }
489
490       delete address;
491       delete recvBuffer;
492
493       recvBufferSize = 0;
494       recvBytes = 0;
495
496       if(s != -1)
497       {
498          FD_CLR(s, &network.readSet);
499          FD_CLR(s, &network.writeSet);
500          FD_CLR(s, &network.exceptSet);
501       }
502
503       disconnectCode = 0;
504
505       // COMMENTED THIS OUT SINCE IT WAS INVALIDATING PROTECTION FOR HTTP FILE CONNECTION REUSE...
506       // WATCH FOR LEAKS IN OTHER PROJECTS?
507       //if(_refCount > 1) _refCount--;
508       if(mustLock) network.mutex.Release();
509    }
510
511    void _Disconnect(DisconnectCode code)
512    {
513       SOCKET s = this.s;
514       network.mutex.Wait();
515
516       disconnectCode = code;
517
518       Free(false);
519       delete this;
520
521       if(s == network.ns - 1)
522          Network_DetermineMaxSocket();
523       network.mutex.Release();
524    }
525
526    bool _Connect(SOCKET s, const char * address, int port)
527    {
528       bool result = false;
529       if(this)
530       {
531          SOCKADDR_IN a;
532          a.sin_family = AF_INET;
533          a.sin_port = htons((uint16)port);
534
535          network.mutex.Wait();
536
537          delete this.address;
538
539          // incref this;
540          destroyed = false;
541          this.address = new char[strlen(address)+1];
542          strcpy(this.address, address);
543          this.a = a;
544          this.s = s;
545          service = null;
546          disconnected = false;
547          disconnectCode = (DisconnectCode)-1;
548          connectThread = null;
549          _connected = -2;
550
551          FD_SET(s, &network.writeSet);
552          if(s >= network.ns && !processAlone)
553          {
554             network.ns = (int)(s+1);
555             network.socketsSemaphore.Release();
556          }
557          connectThread = SocketConnectThread { socket = this };
558
559          if(OnConnect == Socket::OnConnect)
560          {
561             result = true;
562
563             network.mutex.Release();
564             connectThread.Main();
565             network.mutex.Wait();
566
567             if(_connected == -1 || destroyed)
568             {
569                _connected = 0;
570
571                if(s == network.ns - 1)
572                   Network_DetermineMaxSocket();
573 #if 0
574                if(this.disconnectCode == resolveFailed)
575                   Logf("Error resolving address %s\n", this.address);
576 #endif
577                // Free();
578                this.s = -1;
579                result = false;
580             }
581             else if(_connected == 1)
582             {
583                FD_CLR(s, &network.writeSet);
584                FD_SET(s, &network.readSet);
585                FD_SET(s, &network.exceptSet);
586                network.sockets.Add(this);
587
588                incref this;
589                result = true;
590             }
591             else
592                this.s = -1;
593
594             delete connectThread;
595          }
596          else
597          {
598             network.connectSockets.Add(this);
599             incref this;
600             incref connectThread;
601             connectThread.Create();
602             result = true;
603          }
604          /*if(_refCount > 1)
605             delete this;*/
606       }
607       network.mutex.Release();
608       return result;
609    }
610
611    #define MAX_RECEIVE  65536
612
613    bool ProcessSocket(fd_set * rs, fd_set * ws, fd_set * es)
614    {
615       bool result = false;
616       SOCKET s;
617
618       incref this;
619       mutex.Wait();
620       // network.mutex.Wait();
621       s = this.s;
622       if(FD_ISSET(s, rs) || leftOver)
623       {
624          int count = 0;
625
626          result = true;
627          if((int)recvBufferSize - recvBytes < MAX_RECEIVE)
628          {
629             recvBuffer = renew recvBuffer byte[recvBufferSize + MAX_RECEIVE];
630             recvBufferSize += MAX_RECEIVE;
631          }
632
633          if(FD_ISSET(s, rs) && disconnectCode == (DisconnectCode)-1)
634          {
635             if(type == tcp /*|| _connected*/)
636                count = ReceiveData(recvBuffer + recvBytes, recvBufferSize - recvBytes, 0);
637             else
638             {
639                SOCKLEN_TYPE len = sizeof(a);
640                count = (int)recvfrom(s, (char *)recvBuffer + recvBytes,
641                   recvBufferSize - recvBytes, 0, (SOCKADDR *)&a, &len);
642                strcpy(inetAddress, inet_ntoa(this.a.sin_addr));
643                inetPort = ntohs((uint16)a.sin_port);
644             }
645             switch(count)
646             {
647                case 0:
648                   disconnectCode = remoteClosed;
649                   break;
650                case -1:
651                {
652                   /*int yo = errno;
653                   printf("Errno is %d", errno);*/
654                   disconnectCode = remoteLost;
655                   break;
656                }
657             }
658          }
659
660          if(count > 0 || (leftOver && !count))
661          {
662             uint flushCount;
663             leftOver = false;
664             recvBytes += count;
665             for(flushCount = 0; flushCount < recvBytes; )
666             {
667                uint recvCount = OnReceive(recvBuffer + flushCount, recvBytes - flushCount);
668                if(!recvCount)
669                {
670                   leftOver = true;
671                   if(!processAlone)
672                      network.leftOverBytes = true;
673                   break;
674                }
675                flushCount += recvCount;
676             }
677
678             if(flushCount < recvBytes)
679             {
680                if(flushCount)
681                {
682                   memmove(recvBuffer, recvBuffer + flushCount, recvBytes - flushCount);
683                   recvBytes -= flushCount;
684                }
685                else
686                {
687                   // If nothing was acknowledged, clear socket so that OnReceive doesn't keep getting called
688                   if(disconnectCode > -1)
689                      recvBytes = 0;
690                }
691             }
692             else
693                recvBytes = 0;
694          }
695       }
696       else if(FD_ISSET(s, es))
697       {
698          result = true;
699 #if 0
700          Logf("Remote Lost %s\n", (void *)inet_ntoa(a.sin_addr));
701 #endif
702          if(type != udp)
703             _Disconnect(remoteLost);
704       }
705
706       // Disconnect it here
707       if(!recvBytes && disconnectCode > -1 && _connected)
708       {
709          result = true;
710 #if 0
711          if(disconnectCode)
712             Logf("Disconnected (%d) %s\n", disconnectCode, (void *)inet_ntoa(a.sin_addr));
713 #endif
714          if(type != udp)
715             _Disconnect(disconnectCode);
716       }
717       // network.mutex.Release();
718       mutex.Release();
719
720       delete this;
721       return result;
722    }
723 #endif
724
725    public bool Process()
726    {
727       return ProcessTimeOut(0);
728    }
729
730    public bool ProcessTimeOut(Seconds timeOut)
731    {
732       bool gotEvent = false;
733       struct timeval tv = {0, 0};
734       struct timeval tvTO = {(uint)timeOut, (uint)((timeOut -(uint)timeOut)* 1000000)};
735       fd_set rs, ws, es;
736       int selectResult;
737
738       if(disconnectCode > 0 && !leftOver) return false;
739       FD_ZERO(&rs);
740       FD_ZERO(&ws);
741       FD_ZERO(&es);
742       FD_SET(s, &rs);
743       //FD_SET(s, &ws);
744       FD_SET(s, &es);
745
746       incref this;
747       selectResult = select((int)(s+1), &rs, &ws, &es, leftOver ? &tv : (timeOut ? &tvTO : null));
748       mutex.Wait();
749       if(s != -1 && _refCount && (leftOver || selectResult))
750       {
751          gotEvent |= ProcessSocket(&rs, &ws, &es);
752       }
753       mutex.Release();
754       delete this;
755       return gotEvent;
756    }
757
758    Service service;
759    bool leftOver;
760
761    char inetAddress[20];
762    int inetPort;
763
764    Socket prev, next;
765
766    SOCKET s;
767    char * address;
768    Thread connectThread;
769    DisconnectCode disconnectCode;
770    bool destroyed;
771    int _connected;         // -2: Initial value when calling Connect(), -1: Disconnected or otherwise destroyed while connecting, 1: succesfully connected, 0: no longer in any connect/sockets list
772    bool disconnected;
773
774    // Receiving Buffer
775    byte * recvBuffer;
776    uint recvBytes;
777    uint recvBufferSize;
778    SocketType type;
779    bool processAlone;
780    SOCKADDR_IN a;
781    Mutex mutex { };
782 };