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