eventmachine 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,10 +51,10 @@ evma_release_library
51
51
 
52
52
  extern "C" void evma_release_library()
53
53
  {
54
- if (!EventMachine)
55
- throw std::runtime_error ("not initialized");
56
- delete EventMachine;
57
- EventMachine = NULL;
54
+ if (!EventMachine)
55
+ throw std::runtime_error ("not initialized");
56
+ delete EventMachine;
57
+ EventMachine = NULL;
58
58
  }
59
59
 
60
60
 
@@ -105,6 +105,18 @@ extern "C" const char *evma_create_tcp_server (const char *address, int port)
105
105
  return EventMachine->CreateTcpServer (address, port);
106
106
  }
107
107
 
108
+ /*************************
109
+ evma_open_datagram_socket
110
+ *************************/
111
+
112
+ extern "C" const char *evma_open_datagram_socket (const char *address, int port)
113
+ {
114
+ if (!EventMachine)
115
+ throw std::runtime_error ("not initialized");
116
+ return EventMachine->OpenDatagramSocket (address, port);
117
+ }
118
+
119
+
108
120
 
109
121
  /****************************
110
122
  evma_send_data_to_connection
@@ -117,6 +129,17 @@ extern "C" int evma_send_data_to_connection (const char *binding, const char *da
117
129
  return ConnectionDescriptor::SendDataToConnection (binding, data, data_length);
118
130
  }
119
131
 
132
+ /******************
133
+ evma_send_datagram
134
+ ******************/
135
+
136
+ extern "C" int evma_send_datagram (const char *binding, const char *data, int data_length, const char *address, int port)
137
+ {
138
+ if (!EventMachine)
139
+ throw std::runtime_error ("not initialized");
140
+ return DatagramDescriptor::SendDatagram (binding, data, data_length, address, port);
141
+ }
142
+
120
143
 
121
144
  /*********************
122
145
  evma_close_connection
@@ -143,4 +166,3 @@ extern "C" void evma_stop_machine()
143
166
 
144
167
 
145
168
 
146
-
data/ext/ed.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.cpp 2291 2006-04-14 03:56:18Z francis $
3
+ $Id: ed.cpp 2363 2006-04-23 14:32:40Z francis $
4
4
 
5
5
  File: ed.cpp
6
6
  Date: 06Apr06
@@ -122,6 +122,19 @@ bool EventableDescriptor::ShouldDelete()
122
122
  }
123
123
 
124
124
 
125
+ /**********************************
126
+ EventableDescriptor::ScheduleClose
127
+ **********************************/
128
+
129
+ void EventableDescriptor::ScheduleClose (bool after_writing)
130
+ {
131
+ if (after_writing)
132
+ bCloseAfterWriting = true;
133
+ else
134
+ bCloseNow = true;
135
+ }
136
+
137
+
125
138
  /******************************************
126
139
  ConnectionDescriptor::ConnectionDescriptor
127
140
  ******************************************/
@@ -152,8 +165,14 @@ STATIC: ConnectionDescriptor::SendDataToConnection
152
165
 
153
166
  int ConnectionDescriptor::SendDataToConnection (const char *binding, const char *data, int data_length)
154
167
  {
155
- ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
156
- return cd ? cd->SendOutboundData (data, data_length) : -1;
168
+ // TODO: This is something of a hack, or at least it's a static method of the wrong class.
169
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
170
+ if (cd)
171
+ return cd->SendOutboundData (data, data_length);
172
+ DatagramDescriptor *ds = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
173
+ if (ds)
174
+ return ds->SendOutboundData (data, data_length);
175
+ return -1;
157
176
  }
158
177
 
159
178
 
@@ -163,13 +182,10 @@ STATIC: ConnectionDescriptor::CloseConnection
163
182
 
164
183
  void ConnectionDescriptor::CloseConnection (const char *binding, bool after_writing)
165
184
  {
166
- ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
167
- if (cd) {
168
- if (after_writing)
169
- cd->bCloseAfterWriting = true;
170
- else
171
- cd->bCloseNow = true;
172
- }
185
+ // TODO: This is something of a hack, or at least it's a static method of the wrong class.
186
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
187
+ if (ed)
188
+ ed->ScheduleClose (after_writing);
173
189
  }
174
190
 
175
191
 
@@ -180,23 +196,24 @@ ConnectionDescriptor::SendOutboundData
180
196
 
181
197
  int ConnectionDescriptor::SendOutboundData (const char *data, int length)
182
198
  {
183
- // Highly naive and incomplete implementation.
184
- // There's no throttle for runaways (which should abort only this connection
185
- // and not the whole process), and no coalescing of small pages.
186
-
187
- if (bCloseNow || bCloseAfterWriting)
188
- return 0;
189
-
190
- if (!data && (length > 0))
191
- throw std::runtime_error ("bad outbound data");
192
- char *buffer = (char *) malloc (length + 1);
193
- if (!buffer)
194
- throw std::runtime_error ("no allocation for outbound data");
195
- memcpy (buffer, data, length);
196
- buffer [length] = 0;
197
- OutboundPages.push_back (OutboundPage (buffer, length));
198
- OutboundDataSize += length;
199
- return length;
199
+ // Highly naive and incomplete implementation.
200
+ // There's no throttle for runaways (which should abort only this connection
201
+ // and not the whole process), and no coalescing of small pages.
202
+ // (Well, not so bad, small pages are coalesced in ::Write)
203
+
204
+ if (bCloseNow || bCloseAfterWriting)
205
+ return 0;
206
+
207
+ if (!data && (length > 0))
208
+ throw std::runtime_error ("bad outbound data");
209
+ char *buffer = (char *) malloc (length + 1);
210
+ if (!buffer)
211
+ throw std::runtime_error ("no allocation for outbound data");
212
+ memcpy (buffer, data, length);
213
+ buffer [length] = 0;
214
+ OutboundPages.push_back (OutboundPage (buffer, length));
215
+ OutboundDataSize += length;
216
+ return length;
200
217
  }
201
218
 
202
219
 
@@ -276,7 +293,12 @@ void ConnectionDescriptor::Read()
276
293
  total_bytes_read += r;
277
294
  LastRead = gCurrentLoopTime;
278
295
 
279
- readbuffer [r] = 0; // Impute a null-terminator, just for convenience.
296
+ // Add a null-terminator at the the end of the buffer
297
+ // that we will send to the callback.
298
+ // DO NOT CHANGE THIS. We want to explicitly allow users
299
+ // to be able to depend on this behavior, so they will have
300
+ // the option to do some things faster.
301
+ readbuffer [r] = 0;
280
302
 
281
303
  if (EventCallback)
282
304
  (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, readbuffer, r);
@@ -465,37 +487,37 @@ void AcceptorDescriptor::Read()
465
487
  for (int i=0; i < 10; i++) {
466
488
  int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
467
489
  if (sd == -1) {
468
- // This breaks the loop when we've accepted everything on the kernel queue,
469
- // up to 10 new connections. But what if the *first* accept fails?
470
- // Does that mean anything serious is happening, beyond the situation
471
- // described in the note above?
490
+ // This breaks the loop when we've accepted everything on the kernel queue,
491
+ // up to 10 new connections. But what if the *first* accept fails?
492
+ // Does that mean anything serious is happening, beyond the situation
493
+ // described in the note above?
472
494
  break;
473
495
  }
474
496
 
475
- // Set the newly-accepted socket non-blocking.
476
- // On Windows, this may fail because, weirdly, Windows inherits the non-blocking
477
- // attribute that we applied to the acceptor socket into the accepted one.
497
+ // Set the newly-accepted socket non-blocking.
498
+ // On Windows, this may fail because, weirdly, Windows inherits the non-blocking
499
+ // attribute that we applied to the acceptor socket into the accepted one.
478
500
  int val = fcntl (sd, F_GETFL, 0);
479
501
  if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
480
- shutdown (sd, 1);
481
- close (sd);
482
- continue;
502
+ shutdown (sd, 1);
503
+ close (sd);
504
+ continue;
483
505
  }
484
506
 
485
507
 
486
- // Disable slow-start (Nagle algorithm). Eventually make this configurable.
487
- int one = 1;
488
- setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof(one));
508
+ // Disable slow-start (Nagle algorithm). Eventually make this configurable.
509
+ int one = 1;
510
+ setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof(one));
489
511
 
490
512
 
491
- ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
492
- if (!cd)
493
- throw std::runtime_error ("no newly accepted connection");
494
- if (EventCallback) {
495
- (*EventCallback) (GetBinding().c_str(), EventMachine_t::CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
496
- }
497
- assert (MyEventMachine);
498
- MyEventMachine->Add (cd);
513
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
514
+ if (!cd)
515
+ throw std::runtime_error ("no newly accepted connection");
516
+ if (EventCallback) {
517
+ (*EventCallback) (GetBinding().c_str(), EventMachine_t::CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
518
+ }
519
+ assert (MyEventMachine);
520
+ MyEventMachine->Add (cd);
499
521
  }
500
522
 
501
523
  }
@@ -524,5 +546,232 @@ void AcceptorDescriptor::Heartbeat()
524
546
 
525
547
 
526
548
 
549
+ /**************************************
550
+ DatagramDescriptor::DatagramDescriptor
551
+ **************************************/
552
+
553
+ DatagramDescriptor::DatagramDescriptor (int sd):
554
+ EventableDescriptor (sd)
555
+ {
556
+ }
557
+
558
+
559
+ /***************************************
560
+ DatagramDescriptor::~DatagramDescriptor
561
+ ***************************************/
562
+
563
+ DatagramDescriptor::~DatagramDescriptor()
564
+ {
565
+ // Run down any stranded outbound data.
566
+ for (size_t i=0; i < OutboundPages.size(); i++)
567
+ OutboundPages[i].Free();
568
+ }
569
+
570
+
571
+ /************************
572
+ DatagramDescriptor::Read
573
+ ************************/
574
+
575
+ void DatagramDescriptor::Read()
576
+ {
577
+ int sd = GetSocket();
578
+ assert (sd != -1);
579
+
580
+ // This is an extremely large read buffer.
581
+ // In many cases you wouldn't expect to get any more than 4K.
582
+ char readbuffer [16 * 1024];
583
+
584
+ for (int i=0; i < 10; i++) {
585
+ // Don't read just one buffer and then move on. This is faster
586
+ // if there is a lot of incoming.
587
+ // But don't read indefinitely. Give other sockets a chance to run.
588
+
589
+ struct sockaddr_in sin;
590
+ socklen_t slen = sizeof (sin);
591
+ bzero (&sin, slen);
592
+
593
+ int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen);
594
+ //cerr << "<R:" << r << ">";
595
+
596
+ // In UDP, a zero-length packet is perfectly legal.
597
+ if (r >= 0) {
598
+ LastRead = gCurrentLoopTime;
599
+
600
+ // Add a null-terminator at the the end of the buffer
601
+ // that we will send to the callback.
602
+ // DO NOT CHANGE THIS. We want to explicitly allow users
603
+ // to be able to depend on this behavior, so they will have
604
+ // the option to do some things faster.
605
+ readbuffer [r] = 0;
606
+
607
+
608
+ // Set up a "temporary" return address so that callers can "reply" to us
609
+ // from within the callback we are about to invoke. That means that ordinary
610
+ // calls to "send_data_to_connection" (which is of course misnamed in this
611
+ // case) will result in packets being sent back to the same place that sent
612
+ // us this one.
613
+ // There is a different call (evma_send_datagram) for cases where the caller
614
+ // actually wants to send a packet somewhere else.
615
+
616
+ bzero (&ReturnAddress, sizeof(ReturnAddress));
617
+ memcpy (&ReturnAddress, &sin, slen);
618
+
619
+ if (EventCallback)
620
+ (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, readbuffer, r);
621
+
622
+ }
623
+ else {
624
+ // Basically a would-block, meaning we've everything there is to read.
625
+ break;
626
+ }
627
+
628
+ }
629
+
630
+
631
+ }
632
+
633
+
634
+ /*************************
635
+ DatagramDescriptor::Write
636
+ *************************/
637
+
638
+ void DatagramDescriptor::Write()
639
+ {
640
+ /* It's possible for a socket to select writable and then no longer
641
+ * be writable by the time we get around to writing. The kernel might
642
+ * have used up its available output buffers between the select call
643
+ * and when we get here. So this condition is not an error.
644
+ * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData,
645
+ * but differs in the that the outbound data pages (received from the
646
+ * user) are _message-structured._ That is, we send each of them out
647
+ * one message at a time.
648
+ * TODO, we are currently suppressing the EMSGSIZE error!!!
649
+ */
650
+
651
+ int sd = GetSocket();
652
+ assert (sd != -1);
653
+
654
+ assert (OutboundPages.size() > 0);
655
+
656
+ // Send out up to 10 packets, then cycle the machine.
657
+ for (int i = 0; i < 10; i++) {
658
+ if (OutboundPages.size() <= 0)
659
+ break;
660
+ OutboundPage *op = &(OutboundPages[0]);
661
+
662
+ int s = sendto (sd, op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From));
663
+ int e = errno;
664
+
665
+ OutboundDataSize -= op->Length;
666
+ op->Free();
667
+ OutboundPages.pop_front();
668
+
669
+ if (s == -1) {
670
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
671
+ Close();
672
+ break;
673
+ }
674
+ }
675
+ }
676
+ }
677
+
678
+
679
+ /**********************************
680
+ DatagramDescriptor::SelectForWrite
681
+ **********************************/
682
+
683
+ bool DatagramDescriptor::SelectForWrite()
684
+ {
685
+ return (GetOutboundDataSize() > 0);
686
+ }
687
+
688
+
689
+ /************************************
690
+ DatagramDescriptor::SendOutboundData
691
+ ************************************/
692
+
693
+ int DatagramDescriptor::SendOutboundData (const char *data, int length)
694
+ {
695
+ // This is an exact clone of ConnectionDescriptor::SendOutboundData.
696
+ // That means it needs to move to a common ancestor.
697
+
698
+ if (bCloseNow || bCloseAfterWriting)
699
+ return 0;
700
+
701
+ if (!data && (length > 0))
702
+ throw std::runtime_error ("bad outbound data");
703
+ char *buffer = (char *) malloc (length + 1);
704
+ if (!buffer)
705
+ throw std::runtime_error ("no allocation for outbound data");
706
+ memcpy (buffer, data, length);
707
+ buffer [length] = 0;
708
+ OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress));
709
+ OutboundDataSize += length;
710
+ return length;
711
+ }
712
+
713
+
714
+ /****************************************
715
+ DatagramDescriptor::SendOutboundDatagram
716
+ ****************************************/
717
+
718
+ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port)
719
+ {
720
+ // This is an exact clone of ConnectionDescriptor::SendOutboundData.
721
+ // That means it needs to move to a common ancestor.
722
+ // TODO: Refactor this so there's no overlap with SendOutboundData.
723
+
724
+ if (bCloseNow || bCloseAfterWriting)
725
+ return 0;
726
+
727
+ if (!address || !*address || !port)
728
+ return 0;
729
+
730
+ sockaddr_in pin;
731
+ unsigned long HostAddr;
732
+
733
+ HostAddr = inet_addr (address);
734
+ if (HostAddr == -1) {
735
+ hostent *hp = gethostbyname (address);
736
+ if (!hp)
737
+ return 0;
738
+ HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
739
+ }
740
+
741
+ memset (&pin, 0, sizeof(pin));
742
+ pin.sin_family = AF_INET;
743
+ pin.sin_addr.s_addr = HostAddr;
744
+ pin.sin_port = htons (port);
745
+
746
+
747
+
748
+ if (!data && (length > 0))
749
+ throw std::runtime_error ("bad outbound data");
750
+ char *buffer = (char *) malloc (length + 1);
751
+ if (!buffer)
752
+ throw std::runtime_error ("no allocation for outbound data");
753
+ memcpy (buffer, data, length);
754
+ buffer [length] = 0;
755
+ OutboundPages.push_back (OutboundPage (buffer, length, pin));
756
+ OutboundDataSize += length;
757
+ return length;
758
+ }
759
+
760
+
761
+ /****************************************
762
+ STATIC: DatagramDescriptor::SendDatagram
763
+ ****************************************/
764
+
765
+ int DatagramDescriptor::SendDatagram (const char *binding, const char *data, int length, const char *address, int port)
766
+ {
767
+ DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
768
+ if (dd)
769
+ return dd->SendOutboundDatagram (data, length, address, port);
770
+ else
771
+ return -1;
772
+ }
773
+
774
+
775
+
527
776
 
528
777
 
data/ext/ed.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.h 2291 2006-04-14 03:56:18Z francis $
3
+ $Id: ed.h 2363 2006-04-23 14:32:40Z francis $
4
4
 
5
5
  File: ed.h
6
6
  Date: 06Apr06
@@ -41,38 +41,40 @@ class EventableDescriptor: public Bindable_t
41
41
  EventableDescriptor (int);
42
42
  virtual ~EventableDescriptor();
43
43
 
44
- int GetSocket() {return MySocket;}
45
- void Close();
44
+ int GetSocket() {return MySocket;}
45
+ void Close();
46
46
 
47
47
  virtual void Read() = 0;
48
48
  virtual void Write() = 0;
49
49
  virtual void Heartbeat() = 0;
50
50
 
51
- // These methods tell us whether the descriptor
52
- // should be selected or polled for read/write.
53
- virtual bool SelectForRead() = 0;
54
- virtual bool SelectForWrite() = 0;
51
+ // These methods tell us whether the descriptor
52
+ // should be selected or polled for read/write.
53
+ virtual bool SelectForRead() = 0;
54
+ virtual bool SelectForWrite() = 0;
55
55
 
56
- // are we scheduled for a close, or in an error state, or already closed?
56
+ // are we scheduled for a close, or in an error state, or already closed?
57
57
  bool ShouldDelete();
58
- // Do we have any data to write? This is used by ShouldDelete.
59
- virtual int GetOutboundDataSize() {return 0;}
58
+ // Do we have any data to write? This is used by ShouldDelete.
59
+ virtual int GetOutboundDataSize() {return 0;}
60
60
 
61
- void SetEventCallback (void (*cb)(const char*, int, const char*, int));
61
+ void ScheduleClose (bool after_writing);
62
62
 
63
- protected:
64
- enum {
65
- PendingConnectTimeout = 4 // can easily be made an instance variable
66
- };
63
+ void SetEventCallback (void (*cb)(const char*, int, const char*, int));
67
64
 
68
- void (*EventCallback)(const char*, int, const char*, int);
65
+ protected:
66
+ enum {
67
+ PendingConnectTimeout = 4 // can easily be made an instance variable
68
+ };
69
+
70
+ void (*EventCallback)(const char*, int, const char*, int);
69
71
 
70
- time_t CreatedAt;
71
- time_t LastRead;
72
- time_t LastWritten;
73
- int MySocket;
74
- bool bCloseNow;
75
- bool bCloseAfterWriting;
72
+ time_t CreatedAt;
73
+ time_t LastRead;
74
+ time_t LastWritten;
75
+ int MySocket;
76
+ bool bCloseNow;
77
+ bool bCloseAfterWriting;
76
78
  };
77
79
 
78
80
 
@@ -83,66 +85,109 @@ class ConnectionDescriptor
83
85
 
84
86
  class ConnectionDescriptor: public EventableDescriptor
85
87
  {
86
- public:
87
- ConnectionDescriptor (int);
88
- virtual ~ConnectionDescriptor();
88
+ public:
89
+ ConnectionDescriptor (int);
90
+ virtual ~ConnectionDescriptor();
89
91
 
90
- static int SendDataToConnection (const char*, const char*, int);
91
- static void CloseConnection (const char*, bool);
92
+ static int SendDataToConnection (const char*, const char*, int);
93
+ static void CloseConnection (const char*, bool);
92
94
 
93
- int SendOutboundData (const char*, int);
95
+ int SendOutboundData (const char*, int);
94
96
 
95
- void SetConnectPending (bool f) { bConnectPending = f; }
97
+ void SetConnectPending (bool f) { bConnectPending = f; }
96
98
 
97
99
  virtual void Read();
98
100
  virtual void Write();
99
101
  virtual void Heartbeat();
100
102
 
101
- virtual bool SelectForRead();
102
- virtual bool SelectForWrite();
103
+ virtual bool SelectForRead();
104
+ virtual bool SelectForWrite();
103
105
 
104
- // Do we have any data to write? This is used by ShouldDelete.
105
- virtual int GetOutboundDataSize() {return OutboundDataSize;}
106
+ // Do we have any data to write? This is used by ShouldDelete.
107
+ virtual int GetOutboundDataSize() {return OutboundDataSize;}
106
108
 
107
- protected:
108
- struct OutboundPage {
109
- OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
110
- void Free() {if (Buffer) free ((char*)Buffer); }
111
- const char *Buffer;
112
- int Length;
113
- int Offset;
114
- };
109
+ protected:
110
+ struct OutboundPage {
111
+ OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
112
+ void Free() {if (Buffer) free ((char*)Buffer); }
113
+ const char *Buffer;
114
+ int Length;
115
+ int Offset;
116
+ };
115
117
 
116
- protected:
117
- bool bConnectPending;
118
+ protected:
119
+ bool bConnectPending;
118
120
 
119
- deque<OutboundPage> OutboundPages;
120
- int OutboundDataSize;
121
+ deque<OutboundPage> OutboundPages;
122
+ int OutboundDataSize;
121
123
 
122
124
  private:
123
125
  void _WriteOutboundData();
124
126
  };
125
127
 
126
128
 
129
+ /************************
130
+ class DatagramDescriptor
131
+ ************************/
132
+
133
+ class DatagramDescriptor: public EventableDescriptor
134
+ {
135
+ public:
136
+ DatagramDescriptor (int);
137
+ virtual ~DatagramDescriptor();
138
+
139
+ virtual void Read();
140
+ virtual void Write();
141
+ virtual void Heartbeat() {}
142
+
143
+ virtual bool SelectForRead() {return true;}
144
+ virtual bool SelectForWrite();
145
+
146
+ int SendOutboundData (const char*, int);
147
+ int SendOutboundDatagram (const char*, int, const char*, int);
148
+
149
+ // Do we have any data to write? This is used by ShouldDelete.
150
+ virtual int GetOutboundDataSize() {return OutboundDataSize;}
151
+
152
+ static int SendDatagram (const char*, const char*, int, const char*, int);
153
+
154
+
155
+ protected:
156
+ struct OutboundPage {
157
+ OutboundPage (const char *b, int l, struct sockaddr_in f, int o=0): Buffer(b), Length(l), From(f), Offset(o) {}
158
+ void Free() {if (Buffer) free ((char*)Buffer); }
159
+ const char *Buffer;
160
+ int Length;
161
+ int Offset;
162
+ struct sockaddr_in From;
163
+ };
164
+
165
+ deque<OutboundPage> OutboundPages;
166
+ int OutboundDataSize;
167
+
168
+ struct sockaddr_in ReturnAddress;
169
+ };
170
+
171
+
127
172
  /************************
128
173
  class AcceptorDescriptor
129
174
  ************************/
130
175
 
131
176
  class AcceptorDescriptor: public EventableDescriptor
132
177
  {
133
- public:
134
- AcceptorDescriptor (EventMachine_t*, int);
135
- virtual ~AcceptorDescriptor();
178
+ public:
179
+ AcceptorDescriptor (EventMachine_t*, int);
180
+ virtual ~AcceptorDescriptor();
136
181
 
137
182
  virtual void Read();
138
183
  virtual void Write();
139
184
  virtual void Heartbeat();
140
185
 
141
- virtual bool SelectForRead() {return true;}
142
- virtual bool SelectForWrite() {return false;}
186
+ virtual bool SelectForRead() {return true;}
187
+ virtual bool SelectForWrite() {return false;}
143
188
 
144
- protected:
145
- EventMachine_t *MyEventMachine;
189
+ protected:
190
+ EventMachine_t *MyEventMachine;
146
191
  };
147
192
 
148
193
 
data/ext/em.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.cpp 2291 2006-04-14 03:56:18Z francis $
3
+ $Id: em.cpp 2363 2006-04-23 14:32:40Z francis $
4
4
 
5
5
  File: ed.cpp
6
6
  Date: 06Apr06
@@ -271,7 +271,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
271
271
  * not the Solaris-style, and returns zero with the error code in
272
272
  * the error parameter.
273
273
  * Return the binding-text of the newly-created pending connection,
274
- * or NULL if there was a problem.
274
+ * or NULL if there was a problem.
275
275
  */
276
276
 
277
277
  if (!server || !*server || !port)
@@ -351,13 +351,13 @@ EventMachine_t::CreateTcpServer
351
351
 
352
352
  const char *EventMachine_t::CreateTcpServer (const char *server, int port)
353
353
  {
354
- /* Create a TCP-acceptor (server) socket and add it to the event machine.
355
- * Return the binding of the new acceptor to the caller.
356
- * This binding will be referenced when the new acceptor sends events
357
- * to indicate accepted connections.
358
- */
354
+ /* Create a TCP-acceptor (server) socket and add it to the event machine.
355
+ * Return the binding of the new acceptor to the caller.
356
+ * This binding will be referenced when the new acceptor sends events
357
+ * to indicate accepted connections.
358
+ */
359
359
 
360
- const char *output_binding = NULL;
360
+ const char *output_binding = NULL;
361
361
 
362
362
  struct sockaddr_in sin;
363
363
 
@@ -423,7 +423,7 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
423
423
  if (!ad)
424
424
  throw std::runtime_error ("unable to allocate acceptor");
425
425
  Add (ad);
426
- output_binding = ad->GetBinding().c_str();
426
+ output_binding = ad->GetBinding().c_str();
427
427
  }
428
428
 
429
429
  return output_binding;
@@ -435,6 +435,65 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
435
435
  }
436
436
 
437
437
 
438
+ /**********************************
439
+ EventMachine_t::OpenDatagramSocket
440
+ **********************************/
441
+
442
+ const char *EventMachine_t::OpenDatagramSocket (const char *address, int port)
443
+ {
444
+ const char *output_binding = NULL;
445
+
446
+ int sd = socket (AF_INET, SOCK_DGRAM, 0);
447
+ if (sd == -1)
448
+ goto fail;
449
+ // from here on, early returns must close the socket!
450
+
451
+
452
+ struct sockaddr_in sin;
453
+ memset (&sin, 0, sizeof(sin));
454
+ sin.sin_family = AF_INET;
455
+ sin.sin_port = htons (port);
456
+
457
+
458
+ if (address && *address) {
459
+ sin.sin_addr.s_addr = inet_addr (address);
460
+ if (sin.sin_addr.s_addr == -1) {
461
+ hostent *hp = gethostbyname (address);
462
+ if (hp == NULL)
463
+ goto fail;
464
+ sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
465
+ }
466
+ }
467
+ else
468
+ sin.sin_addr.s_addr = htonl (INADDR_ANY);
469
+
470
+
471
+ // Set the new socket nonblocking.
472
+ {
473
+ int val = fcntl (sd, F_GETFL, 0);
474
+ if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1)
475
+ goto fail;
476
+ }
477
+
478
+ if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0)
479
+ goto fail;
480
+
481
+ { // Looking good.
482
+ DatagramDescriptor *ds = new DatagramDescriptor (sd);
483
+ if (!ds)
484
+ throw std::runtime_error ("unable to allocate datagram-socket");
485
+ Add (ds);
486
+ output_binding = ds->GetBinding().c_str();
487
+ }
488
+
489
+ return output_binding;
490
+
491
+ fail:
492
+ if (sd != -1)
493
+ close (sd);
494
+ return NULL;
495
+ }
496
+
438
497
 
439
498
 
440
499
  /*******************
data/ext/em.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.h 2291 2006-04-14 03:56:18Z francis $
3
+ $Id: em.h 2362 2006-04-23 13:57:09Z francis $
4
4
 
5
5
  File: ed.h
6
6
  Date: 06Apr06
@@ -49,6 +49,7 @@ class EventMachine_t
49
49
  const char *InstallOneshotTimer (int);
50
50
  const char *ConnectToServer (const char *, int);
51
51
  const char *CreateTcpServer (const char *, int);
52
+ const char *OpenDatagramSocket (const char *, int);
52
53
 
53
54
  void Add (EventableDescriptor*);
54
55
 
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: eventmachine.h 2313 2006-04-15 16:12:57Z francis $
3
+ $Id: eventmachine.h 2363 2006-04-23 14:32:40Z francis $
4
4
 
5
5
  File: eventmachine.h
6
6
  Date: 15Apr06
@@ -37,9 +37,12 @@ extern "C" {
37
37
  const char *evma_install_oneshot_timer (int seconds);
38
38
  const char *evma_connect_to_server (const char *server, int port);
39
39
  const char *evma_create_tcp_server (const char *address, int port);
40
+ const char *evma_open_datagram_socket (const char *server, int port);
40
41
  int evma_send_data_to_connection (const char *binding, const char *data, int data_length);
42
+ int evma_send_datagram (const char *binding, const char *data, int data_length, const char *address, int port);
41
43
  void evma_close_connection (const char *binding, int after_writing);
42
44
  void evma_stop_machine();
45
+
43
46
  #if __cplusplus
44
47
  }
45
48
  #endif
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: project.h 2291 2006-04-14 03:56:18Z francis $
3
+ $Id: project.h 2355 2006-04-22 19:03:09Z francis $
4
4
 
5
5
  File: project.h
6
6
  Date: 06Apr06
@@ -188,6 +188,17 @@ static VALUE t_send_data (VALUE self, VALUE signature, VALUE data, VALUE data_le
188
188
  }
189
189
 
190
190
 
191
+ /***************
192
+ t_send_datagram
193
+ ***************/
194
+
195
+ static VALUE t_send_datagram (VALUE self, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port)
196
+ {
197
+ int b = evma_send_datagram (StringValuePtr (signature), StringValuePtr (data), FIX2INT (data_length), StringValuePtr(address), FIX2INT(port));
198
+ return INT2NUM (b);
199
+ }
200
+
201
+
191
202
  /******************
192
203
  t_close_connection
193
204
  ******************/
@@ -212,6 +223,18 @@ static VALUE t_connect_server (VALUE self, VALUE server, VALUE port)
212
223
  return rb_str_new2 (f);
213
224
  }
214
225
 
226
+ /*****************
227
+ t_open_udp_socket
228
+ *****************/
229
+
230
+ static VALUE t_open_udp_socket (VALUE self, VALUE server, VALUE port)
231
+ {
232
+ const char *f = evma_open_datagram_socket (StringValuePtr(server), FIX2INT(port));
233
+ if (!f || !*f)
234
+ rb_raise (rb_eRuntimeError, "no datagram socket");
235
+ return rb_str_new2 (f);
236
+ }
237
+
215
238
 
216
239
 
217
240
  /*****************
@@ -251,10 +274,11 @@ extern "C" void Init_rubyeventmachine()
251
274
  rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1);
252
275
  rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2);
253
276
  rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3);
277
+ rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5);
254
278
  rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2);
255
279
  rb_define_module_function (EmModule, "connect_server", (VALUE(*)(...))t_connect_server, 2);
280
+ rb_define_module_function (EmModule, "open_udp_socket", (VALUE(*)(...))t_open_udp_socket, 2);
256
281
  rb_define_module_function (EmModule, "release_machine", (VALUE(*)(...))t_release_machine, 0);
257
- rb_define_module_function (EmModule, "start_server", (VALUE(*)(...))t_release_machine, 0);
258
282
  rb_define_module_function (EmModule, "stop", (VALUE(*)(...))t_stop, 0);
259
283
 
260
284
  // Why am I missing the rb_ function that converts a class/module name to a VALUE?
@@ -1,4 +1,4 @@
1
- # $Id: eventmachine.rb 2325 2006-04-19 16:10:00Z francis $
1
+ # $Id: eventmachine.rb 2364 2006-04-23 16:11:13Z francis $
2
2
  #
3
3
  # Author:: blackhedd (gmail address: garbagecat20).
4
4
  # Date:: 8 Apr 2006
@@ -174,6 +174,10 @@ module EventMachine
174
174
  release_machine
175
175
  end
176
176
 
177
+ # EventMachine#run_without_threads is semantically identical
178
+ # to EventMachine#run, but it runs somewhat faster.
179
+ # However, it must not be used in applications that spin
180
+ # Ruby threads.
177
181
  def EventMachine::run_without_threads &block
178
182
  EventMachine::run false, &block
179
183
  end
@@ -494,6 +498,71 @@ module EventMachine
494
498
  end
495
499
 
496
500
 
501
+ # EventMachine#open_datagram_socket is for support of UDP-based
502
+ # protocols. Its usage is similar to that of EventMachine#start_server.
503
+ # It takes three parameters: an IP address (which must be valid
504
+ # on the machine which executes the method), a port number,
505
+ # and an optional Module name which will handle the data.
506
+ # This method will create a new UDP (datagram) socket and
507
+ # bind it to the address and port that you specify.
508
+ # The normal callbacks (see EventMachine#start_server) will
509
+ # be called as events of interest occur on the newly-created
510
+ # socket, but there are some differences in how they behave.
511
+ #
512
+ # Connection#receive_data will be called when a datagram packet
513
+ # is received on the socket, but unlike TCP sockets, the message
514
+ # boundaries of the received data will be respected. In other words,
515
+ # if the remote peer sent you a datagram of a particular size,
516
+ # you may rely on Connection#receive_data to give you the
517
+ # exact data in the packet, with the original data length.
518
+ # Also observe that Connection#receive_data may be called with a
519
+ # <i>zero-length</i> data payload, since empty datagrams are permitted
520
+ # in UDP.
521
+ #
522
+ # Connection#send_data is available with UDP packets as with TCP,
523
+ # but there is an important difference. Because UDP communications
524
+ # are <i>connectionless,</i> there is no implicit recipient for the packets you
525
+ # send. Ordinarily you must specify the recipient for each packet you send.
526
+ # However, EventMachine
527
+ # provides for the typical pattern of receiving a UDP datagram
528
+ # from a remote peer, performing some operation, and then sending
529
+ # one or more packets in response to the same remote peer.
530
+ # To support this model easily, just use Connection#send_data
531
+ # in the code that you supply for Connection:receive_data.
532
+ # EventMachine will
533
+ # provide an implicit return address for any messages sent to
534
+ # Connection#send_data within the context of a Connection#receive_data callback,
535
+ # and your response will automatically go to the correct remote peer.
536
+ # (TODO: Example-code needed!)
537
+ #
538
+ # Observe that the port number that you supply to EventMachine#open_datagram_socket
539
+ # may be zero. In this case, EventMachine will create a UDP socket
540
+ # that is bound to an <i>ephemeral</i> (not well-known) port.
541
+ # This is not appropriate for servers that must publish a well-known
542
+ # port to which remote peers may send datagrams. But it can be useful
543
+ # for clients that send datagrams to other servers.
544
+ # If you do this, you will receive any responses from the remote
545
+ # servers through the normal Connection#receive_data callback.
546
+ # Observe that you will probably have issues with firewalls blocking
547
+ # the ephemeral port numbers, so this technique is most appropriate for LANs.
548
+ # (TODO: Need an example!)
549
+ #
550
+ # If you wish to send datagrams to arbitrary remote peers (not
551
+ # necessarily ones that have sent data to which you are responding),
552
+ # then see Connection#send_datagram.
553
+ #
554
+ def self::open_datagram_socket address, port, handler=nil
555
+ s = open_udp_socket address, port
556
+ klass = Class.new( Connection ) {
557
+ handler and include handler
558
+ }
559
+ c = klass.new s
560
+ @conns[s] = c
561
+ block_given? and yield c
562
+ c
563
+ end
564
+
565
+
497
566
  private
498
567
  def EventMachine::event_callback conn_binding, opcode, data
499
568
  case opcode
@@ -666,6 +735,31 @@ class Connection
666
735
  EventMachine::send_data @signature, data, data.length
667
736
  end
668
737
 
738
+
739
+ # send_datagram is for sending UDP messages.
740
+ # This method may be called from any Connection object that refers
741
+ # to an open datagram socket (see EventMachine#open_datagram_socket).
742
+ # The method sends a UDP (datagram) packet containing the data you specify,
743
+ # to a remote peer specified by the IP address and port that you give
744
+ # as parameters to the method.
745
+ # Observe that you may send a zero-length packet (empty string).
746
+ # However, you may not send an arbitrarily-large data packet because
747
+ # your operating system will enforce a platform-specific limit on
748
+ # the size of the outbound packet. (Your kernel
749
+ # will respond in a platform-specific way if you send an overlarge
750
+ # packet: some will send a truncated packet, some will complain, and
751
+ # some will silently drop your request).
752
+ # On LANs, it's usually OK to send datagrams up to about 4000 bytes in length,
753
+ # but to be really safe, send messages smaller than the Ethernet-packet
754
+ # size (typically about 1400 bytes). Some very restrictive WANs
755
+ # will either drop or truncate packets larger than about 500 bytes.
756
+ #
757
+ def send_datagram data, recipient_address, recipient_port
758
+ data = data.to_s
759
+ EventMachine::send_datagram @signature, data, data.length, recipient_address, recipient_port
760
+ end
761
+
762
+
669
763
  end
670
764
 
671
765
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
3
3
  specification_version: 1
4
4
  name: eventmachine
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.2
7
- date: 2006-04-19
6
+ version: 0.4.3
7
+ date: 2006-04-23
8
8
  summary: Ruby/EventMachine socket engine library
9
9
  require_paths:
10
10
  - lib