eventmachine 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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