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