eventmachine 0.4.5 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/RELEASE_NOTES +17 -1
- data/ext/binder.cpp +17 -1
- data/ext/binder.h +1 -1
- data/ext/cmain.cpp +15 -1
- data/ext/ed.cpp +206 -28
- data/ext/ed.h +18 -1
- data/ext/em.cpp +65 -19
- data/ext/em.h +1 -1
- data/ext/eventmachine.h +2 -1
- data/ext/extconf.rb +52 -3
- data/ext/page.cpp +114 -0
- data/ext/page.h +58 -0
- data/ext/project.h +30 -1
- data/ext/rubymain.cpp +89 -2
- data/ext/sigs.cpp +44 -8
- data/ext/sigs.h +5 -1
- data/ext/ssl.cpp +377 -0
- data/ext/ssl.h +92 -0
- data/lib/eventmachine.rb +39 -7
- metadata +6 -2
data/RELEASE_NOTES
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
-
$Id: RELEASE_NOTES
|
1
|
+
$Id: RELEASE_NOTES 2459 2006-05-05 17:11:04Z francis $
|
2
2
|
|
3
3
|
RUBY/EventMachine RELEASE NOTES
|
4
4
|
|
5
|
+
--------------------------------------------------
|
6
|
+
Version: 0.5.1, released 05May06
|
7
|
+
Made it possible to pass a Class rather than a Module
|
8
|
+
to a protocol handler.
|
9
|
+
Added Windows port.
|
10
|
+
|
11
|
+
--------------------------------------------------
|
12
|
+
Version: 0.5.0, released 30Apr06
|
13
|
+
Added a preliminary SSL/TLS extension. This will probably
|
14
|
+
change over the next few releases.
|
15
|
+
|
16
|
+
--------------------------------------------------
|
17
|
+
Version: 0.4.5, released 29Apr06
|
18
|
+
Changed ext files so the ruby.h is installed after unistd.h
|
19
|
+
otherwise it doesn't compile on gcc 4.1
|
20
|
+
|
5
21
|
--------------------------------------------------
|
6
22
|
Version: 0.4.2, released 19Apr06
|
7
23
|
Changed the Ruby-glue so the extension will play nicer
|
data/ext/binder.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: binder.cpp
|
3
|
+
$Id: binder.cpp 2451 2006-05-05 03:16:31Z francis $
|
4
4
|
|
5
5
|
File: binder.cpp
|
6
6
|
Date: 07Apr06
|
@@ -42,6 +42,7 @@ string Bindable_t::CreateBinding()
|
|
42
42
|
static string seed;
|
43
43
|
|
44
44
|
if ((index >= 1000000) || (seed.length() == 0)) {
|
45
|
+
#ifdef OS_UNIX
|
45
46
|
int fd = open (DEV_URANDOM, O_RDONLY);
|
46
47
|
if (fd < 0)
|
47
48
|
throw std::runtime_error ("No entropy device");
|
@@ -58,6 +59,21 @@ string Bindable_t::CreateBinding()
|
|
58
59
|
sprintf (u2 + (i * 2), "%02x", u1[i]);
|
59
60
|
|
60
61
|
seed = string (u2);
|
62
|
+
#endif
|
63
|
+
|
64
|
+
|
65
|
+
#ifdef OS_WIN32
|
66
|
+
UUID uuid;
|
67
|
+
UuidCreate (&uuid);
|
68
|
+
unsigned char *uuidstring = NULL;
|
69
|
+
UuidToString (&uuid, &uuidstring);
|
70
|
+
if (!uuidstring)
|
71
|
+
throw std::runtime_error ("Unable to read uuid");
|
72
|
+
seed = string ((const char*)uuidstring);
|
73
|
+
|
74
|
+
RpcStringFree (&uuidstring);
|
75
|
+
#endif
|
76
|
+
|
61
77
|
index = 0;
|
62
78
|
|
63
79
|
|
data/ext/binder.h
CHANGED
data/ext/cmain.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id:
|
3
|
+
$Id: cmain.cpp 2416 2006-04-29 02:02:16Z francis $
|
4
4
|
|
5
5
|
File: libmain.cpp
|
6
6
|
Date: 06Apr06
|
@@ -165,4 +165,18 @@ extern "C" void evma_stop_machine()
|
|
165
165
|
}
|
166
166
|
|
167
167
|
|
168
|
+
/**************
|
169
|
+
evma_start_tls
|
170
|
+
**************/
|
171
|
+
|
172
|
+
extern "C" void evma_start_tls (const char *binding)
|
173
|
+
{
|
174
|
+
if (!EventMachine)
|
175
|
+
throw std::runtime_error ("not initialized");
|
176
|
+
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
177
|
+
if (ed)
|
178
|
+
ed->StartTls();
|
179
|
+
}
|
180
|
+
|
181
|
+
|
168
182
|
|
data/ext/ed.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: ed.cpp
|
3
|
+
$Id: ed.cpp 2457 2006-05-05 14:57:21Z francis $
|
4
4
|
|
5
5
|
File: ed.cpp
|
6
6
|
Date: 06Apr06
|
@@ -27,6 +27,25 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
27
27
|
#include "project.h"
|
28
28
|
|
29
29
|
|
30
|
+
|
31
|
+
/********************
|
32
|
+
SetSocketNonblocking
|
33
|
+
********************/
|
34
|
+
|
35
|
+
bool SetSocketNonblocking (SOCKET sd)
|
36
|
+
{
|
37
|
+
#ifdef OS_UNIX
|
38
|
+
int val = fcntl (sd, F_GETFL, 0);
|
39
|
+
return (fcntl (sd, F_SETFL, val | O_NONBLOCK) != SOCKET_ERROR) ? true : false;
|
40
|
+
#endif
|
41
|
+
|
42
|
+
#ifdef OS_WIN32
|
43
|
+
unsigned long one = 1;
|
44
|
+
return (ioctlsocket (sd, FIONBIO, &one) == 0) ? true : false;
|
45
|
+
#endif
|
46
|
+
}
|
47
|
+
|
48
|
+
|
30
49
|
/****************************************
|
31
50
|
EventableDescriptor::EventableDescriptor
|
32
51
|
****************************************/
|
@@ -60,7 +79,7 @@ EventableDescriptor::EventableDescriptor (int sd):
|
|
60
79
|
* effect of discarding outbound data.
|
61
80
|
*/
|
62
81
|
|
63
|
-
if (sd ==
|
82
|
+
if (sd == INVALID_SOCKET)
|
64
83
|
throw std::runtime_error ("bad eventable descriptor");
|
65
84
|
CreatedAt = gCurrentLoopTime;
|
66
85
|
}
|
@@ -95,10 +114,10 @@ EventableDescriptor::Close
|
|
95
114
|
void EventableDescriptor::Close()
|
96
115
|
{
|
97
116
|
// Close the socket right now. Intended for emergencies.
|
98
|
-
if (MySocket !=
|
117
|
+
if (MySocket != INVALID_SOCKET) {
|
99
118
|
shutdown (MySocket, 1);
|
100
|
-
|
101
|
-
MySocket =
|
119
|
+
closesocket (MySocket);
|
120
|
+
MySocket = INVALID_SOCKET;
|
102
121
|
}
|
103
122
|
}
|
104
123
|
|
@@ -118,7 +137,7 @@ bool EventableDescriptor::ShouldDelete()
|
|
118
137
|
* if there is outbound data to write, and only request a close if there is none.
|
119
138
|
*/
|
120
139
|
|
121
|
-
return ((MySocket ==
|
140
|
+
return ((MySocket == INVALID_SOCKET) || bCloseNow || (bCloseAfterWriting && (GetOutboundDataSize() <= 0)));
|
122
141
|
}
|
123
142
|
|
124
143
|
|
@@ -142,7 +161,9 @@ ConnectionDescriptor::ConnectionDescriptor
|
|
142
161
|
ConnectionDescriptor::ConnectionDescriptor (int sd):
|
143
162
|
EventableDescriptor (sd),
|
144
163
|
bConnectPending (false),
|
145
|
-
OutboundDataSize (0)
|
164
|
+
OutboundDataSize (0),
|
165
|
+
SslBox (NULL),
|
166
|
+
bIsServer (false)
|
146
167
|
{
|
147
168
|
}
|
148
169
|
|
@@ -156,6 +177,9 @@ ConnectionDescriptor::~ConnectionDescriptor()
|
|
156
177
|
// Run down any stranded outbound data.
|
157
178
|
for (size_t i=0; i < OutboundPages.size(); i++)
|
158
179
|
OutboundPages[i].Free();
|
180
|
+
|
181
|
+
if (SslBox)
|
182
|
+
delete SslBox;
|
159
183
|
}
|
160
184
|
|
161
185
|
|
@@ -196,6 +220,36 @@ ConnectionDescriptor::SendOutboundData
|
|
196
220
|
|
197
221
|
int ConnectionDescriptor::SendOutboundData (const char *data, int length)
|
198
222
|
{
|
223
|
+
if (SslBox) {
|
224
|
+
if (length > 0) {
|
225
|
+
int w = SslBox->PutPlaintext (data, length);
|
226
|
+
if (w < 0)
|
227
|
+
ScheduleClose (false);
|
228
|
+
else
|
229
|
+
_DispatchCiphertext();
|
230
|
+
}
|
231
|
+
// TODO: What's the correct return value?
|
232
|
+
return 1; // That's a wild guess, almost certainly wrong.
|
233
|
+
}
|
234
|
+
else
|
235
|
+
return _SendRawOutboundData (data, length);
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
/******************************************
|
241
|
+
ConnectionDescriptor::_SendRawOutboundData
|
242
|
+
******************************************/
|
243
|
+
|
244
|
+
int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
|
245
|
+
{
|
246
|
+
/* This internal method is called to schedule bytes that
|
247
|
+
* will be sent out to the remote peer.
|
248
|
+
* It's not directly accessed by the caller, who hits ::SendOutboundData,
|
249
|
+
* which may or may not filter or encrypt the caller's data before
|
250
|
+
* sending it here.
|
251
|
+
*/
|
252
|
+
|
199
253
|
// Highly naive and incomplete implementation.
|
200
254
|
// There's no throttle for runaways (which should abort only this connection
|
201
255
|
// and not the whole process), and no coalescing of small pages.
|
@@ -276,7 +330,7 @@ void ConnectionDescriptor::Read()
|
|
276
330
|
*/
|
277
331
|
|
278
332
|
int sd = GetSocket();
|
279
|
-
assert (sd !=
|
333
|
+
assert (sd != INVALID_SOCKET);
|
280
334
|
|
281
335
|
int total_bytes_read = 0;
|
282
336
|
char readbuffer [16 * 1024];
|
@@ -299,10 +353,7 @@ void ConnectionDescriptor::Read()
|
|
299
353
|
// to be able to depend on this behavior, so they will have
|
300
354
|
// the option to do some things faster.
|
301
355
|
readbuffer [r] = 0;
|
302
|
-
|
303
|
-
if (EventCallback)
|
304
|
-
(*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, readbuffer, r);
|
305
|
-
|
356
|
+
_DispatchInboundData (readbuffer, r);
|
306
357
|
}
|
307
358
|
else if (r == 0) {
|
308
359
|
break;
|
@@ -324,6 +375,36 @@ void ConnectionDescriptor::Read()
|
|
324
375
|
}
|
325
376
|
|
326
377
|
|
378
|
+
|
379
|
+
/******************************************
|
380
|
+
ConnectionDescriptor::_DispatchInboundData
|
381
|
+
******************************************/
|
382
|
+
|
383
|
+
void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
|
384
|
+
{
|
385
|
+
if (SslBox) {
|
386
|
+
SslBox->PutCiphertext (buffer, size);
|
387
|
+
|
388
|
+
int s;
|
389
|
+
char B [2048];
|
390
|
+
while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) {
|
391
|
+
B [s] = 0;
|
392
|
+
if (EventCallback)
|
393
|
+
(*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, B, s);
|
394
|
+
}
|
395
|
+
// INCOMPLETE, s may indicate an SSL error that would force the connection down.
|
396
|
+
_DispatchCiphertext();
|
397
|
+
}
|
398
|
+
else {
|
399
|
+
if (EventCallback)
|
400
|
+
(*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, buffer, size);
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
404
|
+
|
405
|
+
|
406
|
+
|
407
|
+
|
327
408
|
/***************************
|
328
409
|
ConnectionDescriptor::Write
|
329
410
|
***************************/
|
@@ -335,13 +416,23 @@ void ConnectionDescriptor::Write()
|
|
335
416
|
* At that point, check to be sure there are no errors,
|
336
417
|
* and if none, then promote the socket out of the pending
|
337
418
|
* state.
|
419
|
+
* TODO: I haven't figured out how Windows signals errors on
|
420
|
+
* unconnected sockets. Maybe it does the untraditional but
|
421
|
+
* logical thing and makes the socket selectable for error.
|
422
|
+
* If so, it's unsupported here for the time being, and connect
|
423
|
+
* errors will have to be caught by the timeout mechanism.
|
338
424
|
*/
|
339
425
|
|
340
426
|
if (bConnectPending) {
|
341
427
|
int error;
|
342
428
|
socklen_t len;
|
343
429
|
len = sizeof(error);
|
430
|
+
#ifdef OS_UNIX
|
344
431
|
int o = getsockopt (MySocket, SOL_SOCKET, SO_ERROR, &error, &len);
|
432
|
+
#endif
|
433
|
+
#ifdef OS_WIN32
|
434
|
+
int o = getsockopt (MySocket, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
|
435
|
+
#endif
|
345
436
|
if ((o == 0) && (error == 0))
|
346
437
|
bConnectPending = false;
|
347
438
|
else
|
@@ -367,7 +458,7 @@ void ConnectionDescriptor::_WriteOutboundData()
|
|
367
458
|
*/
|
368
459
|
|
369
460
|
int sd = GetSocket();
|
370
|
-
assert (sd !=
|
461
|
+
assert (sd != INVALID_SOCKET);
|
371
462
|
|
372
463
|
char output_buffer [16 * 1024];
|
373
464
|
size_t nbytes = 0;
|
@@ -394,7 +485,7 @@ void ConnectionDescriptor::_WriteOutboundData()
|
|
394
485
|
// if it were we probably would have crashed already.
|
395
486
|
assert (nbytes > 0);
|
396
487
|
|
397
|
-
assert (GetSocket() !=
|
488
|
+
assert (GetSocket() != INVALID_SOCKET);
|
398
489
|
int bytes_written = send (GetSocket(), output_buffer, nbytes, 0);
|
399
490
|
|
400
491
|
if (bytes_written > 0) {
|
@@ -410,13 +501,97 @@ void ConnectionDescriptor::_WriteOutboundData()
|
|
410
501
|
}
|
411
502
|
}
|
412
503
|
else {
|
504
|
+
#ifdef OS_UNIX
|
413
505
|
if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
|
506
|
+
#endif
|
507
|
+
#ifdef OS_WIN32
|
508
|
+
if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
|
509
|
+
#endif
|
414
510
|
Close();
|
415
511
|
}
|
416
512
|
}
|
417
513
|
|
418
514
|
|
419
515
|
|
516
|
+
/******************************
|
517
|
+
ConnectionDescriptor::StartTls
|
518
|
+
******************************/
|
519
|
+
|
520
|
+
void ConnectionDescriptor::StartTls()
|
521
|
+
{
|
522
|
+
if (SslBox)
|
523
|
+
throw std::runtime_error ("SSL/TLS already running on connection");
|
524
|
+
|
525
|
+
SslBox = new SslBox_t (bIsServer);
|
526
|
+
_DispatchCiphertext();
|
527
|
+
}
|
528
|
+
|
529
|
+
|
530
|
+
|
531
|
+
/*****************************************
|
532
|
+
ConnectionDescriptor::_DispatchCiphertext
|
533
|
+
*****************************************/
|
534
|
+
|
535
|
+
void ConnectionDescriptor::_DispatchCiphertext()
|
536
|
+
{
|
537
|
+
assert (SslBox);
|
538
|
+
|
539
|
+
|
540
|
+
char BigBuf [2048];
|
541
|
+
bool did_work;
|
542
|
+
|
543
|
+
do {
|
544
|
+
did_work = false;
|
545
|
+
|
546
|
+
// try to drain ciphertext
|
547
|
+
while (SslBox->CanGetCiphertext()) {
|
548
|
+
int r = SslBox->GetCiphertext (BigBuf, sizeof(BigBuf));
|
549
|
+
assert (r > 0);
|
550
|
+
_SendRawOutboundData (BigBuf, r);
|
551
|
+
did_work = true;
|
552
|
+
}
|
553
|
+
|
554
|
+
// Pump the SslBox, in case it has queued outgoing plaintext
|
555
|
+
// This will return >0 if data was written,
|
556
|
+
// 0 if no data was written, and <0 if there was a fatal error.
|
557
|
+
bool pump;
|
558
|
+
do {
|
559
|
+
pump = false;
|
560
|
+
int w = SslBox->PutPlaintext (NULL, 0);
|
561
|
+
if (w > 0) {
|
562
|
+
did_work = true;
|
563
|
+
pump = true;
|
564
|
+
}
|
565
|
+
else if (w < 0)
|
566
|
+
ScheduleClose (false);
|
567
|
+
} while (pump);
|
568
|
+
|
569
|
+
// try to put plaintext. INCOMPLETE, doesn't belong here?
|
570
|
+
// In SendOutboundData, we're spooling plaintext directly
|
571
|
+
// into SslBox. That may be wrong, we may need to buffer it
|
572
|
+
// up here!
|
573
|
+
/*
|
574
|
+
const char *ptr;
|
575
|
+
int ptr_length;
|
576
|
+
while (OutboundPlaintext.GetPage (&ptr, &ptr_length)) {
|
577
|
+
assert (ptr && (ptr_length > 0));
|
578
|
+
int w = SslMachine.PutPlaintext (ptr, ptr_length);
|
579
|
+
if (w > 0) {
|
580
|
+
OutboundPlaintext.DiscardBytes (w);
|
581
|
+
did_work = true;
|
582
|
+
}
|
583
|
+
else
|
584
|
+
break;
|
585
|
+
}
|
586
|
+
*/
|
587
|
+
|
588
|
+
} while (did_work);
|
589
|
+
|
590
|
+
}
|
591
|
+
|
592
|
+
|
593
|
+
|
594
|
+
|
420
595
|
/*******************************
|
421
596
|
ConnectionDescriptor::Heartbeat
|
422
597
|
*******************************/
|
@@ -486,7 +661,7 @@ void AcceptorDescriptor::Read()
|
|
486
661
|
|
487
662
|
for (int i=0; i < 10; i++) {
|
488
663
|
int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
|
489
|
-
if (sd ==
|
664
|
+
if (sd == INVALID_SOCKET) {
|
490
665
|
// This breaks the loop when we've accepted everything on the kernel queue,
|
491
666
|
// up to 10 new connections. But what if the *first* accept fails?
|
492
667
|
// Does that mean anything serious is happening, beyond the situation
|
@@ -497,10 +672,11 @@ void AcceptorDescriptor::Read()
|
|
497
672
|
// Set the newly-accepted socket non-blocking.
|
498
673
|
// On Windows, this may fail because, weirdly, Windows inherits the non-blocking
|
499
674
|
// attribute that we applied to the acceptor socket into the accepted one.
|
500
|
-
|
501
|
-
|
675
|
+
if (!SetSocketNonblocking (sd)) {
|
676
|
+
//int val = fcntl (sd, F_GETFL, 0);
|
677
|
+
//if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
|
502
678
|
shutdown (sd, 1);
|
503
|
-
|
679
|
+
closesocket (sd);
|
504
680
|
continue;
|
505
681
|
}
|
506
682
|
|
@@ -513,6 +689,7 @@ void AcceptorDescriptor::Read()
|
|
513
689
|
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
514
690
|
if (!cd)
|
515
691
|
throw std::runtime_error ("no newly accepted connection");
|
692
|
+
cd->SetServerMode();
|
516
693
|
if (EventCallback) {
|
517
694
|
(*EventCallback) (GetBinding().c_str(), EventMachine_t::CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
|
518
695
|
}
|
@@ -554,7 +731,7 @@ DatagramDescriptor::DatagramDescriptor (int sd):
|
|
554
731
|
EventableDescriptor (sd),
|
555
732
|
OutboundDataSize (0)
|
556
733
|
{
|
557
|
-
|
734
|
+
memset (&ReturnAddress, 0, sizeof(ReturnAddress));
|
558
735
|
}
|
559
736
|
|
560
737
|
|
@@ -577,7 +754,7 @@ DatagramDescriptor::Read
|
|
577
754
|
void DatagramDescriptor::Read()
|
578
755
|
{
|
579
756
|
int sd = GetSocket();
|
580
|
-
assert (sd !=
|
757
|
+
assert (sd != INVALID_SOCKET);
|
581
758
|
|
582
759
|
// This is an extremely large read buffer.
|
583
760
|
// In many cases you wouldn't expect to get any more than 4K.
|
@@ -590,7 +767,7 @@ void DatagramDescriptor::Read()
|
|
590
767
|
|
591
768
|
struct sockaddr_in sin;
|
592
769
|
socklen_t slen = sizeof (sin);
|
593
|
-
|
770
|
+
memset (&sin, 0, slen);
|
594
771
|
|
595
772
|
int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen);
|
596
773
|
//cerr << "<R:" << r << ">";
|
@@ -615,7 +792,7 @@ void DatagramDescriptor::Read()
|
|
615
792
|
// There is a different call (evma_send_datagram) for cases where the caller
|
616
793
|
// actually wants to send a packet somewhere else.
|
617
794
|
|
618
|
-
|
795
|
+
memset (&ReturnAddress, 0, sizeof(ReturnAddress));
|
619
796
|
memcpy (&ReturnAddress, &sin, slen);
|
620
797
|
|
621
798
|
if (EventCallback)
|
@@ -651,7 +828,7 @@ void DatagramDescriptor::Write()
|
|
651
828
|
*/
|
652
829
|
|
653
830
|
int sd = GetSocket();
|
654
|
-
assert (sd !=
|
831
|
+
assert (sd != INVALID_SOCKET);
|
655
832
|
|
656
833
|
assert (OutboundPages.size() > 0);
|
657
834
|
|
@@ -668,8 +845,13 @@ void DatagramDescriptor::Write()
|
|
668
845
|
op->Free();
|
669
846
|
OutboundPages.pop_front();
|
670
847
|
|
671
|
-
if (s ==
|
848
|
+
if (s == SOCKET_ERROR) {
|
849
|
+
#ifdef OS_UNIX
|
672
850
|
if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
|
851
|
+
#endif
|
852
|
+
#ifdef OS_WIN32
|
853
|
+
if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
|
854
|
+
#endif
|
673
855
|
Close();
|
674
856
|
break;
|
675
857
|
}
|
@@ -773,7 +955,3 @@ int DatagramDescriptor::SendDatagram (const char *binding, const char *data, int
|
|
773
955
|
return -1;
|
774
956
|
}
|
775
957
|
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|