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