eventmachine 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/cmain.cpp +27 -5
- data/ext/ed.cpp +298 -49
- data/ext/ed.h +97 -52
- data/ext/em.cpp +68 -9
- data/ext/em.h +2 -1
- data/ext/eventmachine.h +4 -1
- data/ext/project.h +1 -1
- data/ext/rubymain.cpp +25 -1
- data/lib/eventmachine.rb +95 -1
- metadata +2 -2
data/ext/cmain.cpp
CHANGED
@@ -51,10 +51,10 @@ evma_release_library
|
|
51
51
|
|
52
52
|
extern "C" void evma_release_library()
|
53
53
|
{
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
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
|
-
|
156
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
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
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
|
-
|
476
|
-
|
477
|
-
|
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
|
-
|
481
|
-
|
482
|
-
|
502
|
+
shutdown (sd, 1);
|
503
|
+
close (sd);
|
504
|
+
continue;
|
483
505
|
}
|
484
506
|
|
485
507
|
|
486
|
-
|
487
|
-
|
488
|
-
|
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
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
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
|
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
|
-
|
45
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
56
|
+
// are we scheduled for a close, or in an error state, or already closed?
|
57
57
|
bool ShouldDelete();
|
58
|
-
|
59
|
-
|
58
|
+
// Do we have any data to write? This is used by ShouldDelete.
|
59
|
+
virtual int GetOutboundDataSize() {return 0;}
|
60
60
|
|
61
|
-
|
61
|
+
void ScheduleClose (bool after_writing);
|
62
62
|
|
63
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
88
|
+
public:
|
89
|
+
ConnectionDescriptor (int);
|
90
|
+
virtual ~ConnectionDescriptor();
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
+
static int SendDataToConnection (const char*, const char*, int);
|
93
|
+
static void CloseConnection (const char*, bool);
|
92
94
|
|
93
|
-
|
95
|
+
int SendOutboundData (const char*, int);
|
94
96
|
|
95
|
-
|
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
|
-
|
102
|
-
|
103
|
+
virtual bool SelectForRead();
|
104
|
+
virtual bool SelectForWrite();
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
+
// Do we have any data to write? This is used by ShouldDelete.
|
107
|
+
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
106
108
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
117
|
-
|
118
|
+
protected:
|
119
|
+
bool bConnectPending;
|
118
120
|
|
119
|
-
|
120
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
142
|
-
|
186
|
+
virtual bool SelectForRead() {return true;}
|
187
|
+
virtual bool SelectForWrite() {return false;}
|
143
188
|
|
144
|
-
|
145
|
-
|
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
|
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
|
-
|
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
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
|
data/ext/eventmachine.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: eventmachine.h
|
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
|
data/ext/project.h
CHANGED
data/ext/rubymain.cpp
CHANGED
@@ -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?
|
data/lib/eventmachine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: eventmachine.rb
|
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