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