4
* 2001-January-28 Jason Rohrer
7
* 2001-February-4 Jason Rohrer
8
* Fixed receive so that it waits for all requested bytes to arrive.
10
* 2001-March-4 Jason Rohrer
11
* Replaced include of <winbase.h> and <windef.h> with <windows.h>
12
* to fix compile bugs encountered with newer windows compilers.
14
* 2001-May-12 Jason Rohrer
15
* Fixed a bug in socket receive error checking.
17
* 2001-November-13 Jason Rohrer
18
* Changed timeout parameter to signed, since -1 is a possible argument.
20
* 2002-April-15 Jason Rohrer
21
* Removed call to WSAGetLastError, since it seems to pick up errors from
22
* non-socket calls. For example, if sys/stat.h stat() is called on a file
23
* that does not exist, WSAGetLastError returns 2, which is not even a
24
* winsock error code. We should probably report this bug, huh?
26
* 2002-August-2 Jason Rohrer
27
* Added functon for getting remote host address, but no implementation.
29
* 2002-August-5 Jason Rohrer
30
* Added implementation of getRemoteHostAddress().
32
* 2002-September-8 Jason Rohrer
33
* Fixed a major looping bug with broken sockets.
35
* 2002-November-15 Jason Rohrer
36
* Fixed a security hole when getting the remote host address.
38
* 2003-February-4 Jason Rohrer
39
* Added a function for getting the local host address from a socket.
40
* Still need to test the win32 version of this.
42
* 2003-February-5 Jason Rohrer
43
* Fixed a bug in call to gethostname. Removed unused variable.
45
* 2004-January-4 Jason Rohrer
46
* Added use of network function locks.
48
* 2004-January-11 Jason Rohrer
49
* Fixed a bug in handling of timeout return value.
51
* 2004-March-23 Jason Rohrer
52
* Removed timeout error message.
54
* 2004-December-13 Jason Rohrer
55
* Added a breakConnection function.
57
* 2004-December-24 Jason Rohrer
58
* Fixed bug in close call.
60
* 2005-July-5 Jason Rohrer
61
* Added port number when getting address of remote host.
63
* 2006-May-28 Jason Rohrer
64
* Changed timeout behavior slightly to support emulation of non-blocking mode.
66
* 2006-June-5 Jason Rohrer
67
* Added support for non-blocking sends.
69
* 2008-September-30 Jason Rohrer
70
* Added support for non-blocking connect.
71
* Fixed close-detection in non-blocking read.
76
#include "minorGems/network/Socket.h"
77
#include "minorGems/network/NetworkFunctionLocks.h"
87
int timed_read( int inSock, unsigned char *inBuf,
88
int inLen, long inMilliseconds );
92
* Windows-specific implementation of the Socket class member functions.
98
// Win32 does not define socklen_t
99
typedef int socklen_t;
103
char Socket::sInitialized = false;
107
int Socket::initSocketFramework() {
108
WORD wVersionRequested;
112
wVersionRequested = MAKEWORD( 1, 0 );
113
err = WSAStartup( wVersionRequested, &wsaData );
116
// no usable DLL found
117
printf( "WinSock DLL version 1.0 or higher not found.\n" );
130
int *socketIDptr = (int *)( mNativeObjectPointer );
131
int socketID = socketIDptr[0];
133
if( !mIsConnectionBroken ) {
135
// 2 specifies shutting down both sends and receives
136
shutdown( socketID, 2 );
137
mIsConnectionBroken = true;
140
closesocket( socketID );
142
delete [] socketIDptr;
147
int Socket::isConnected() {
153
int *socketIDptr = (int *)( mNativeObjectPointer );
154
int socketID = socketIDptr[0];
163
FD_SET( socketID, &fsr );
165
// check if connection event waiting right now
170
ret = select( socketID + 1, NULL, &fsr, NULL, &tv );
181
ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len );
201
int Socket::send( unsigned char *inBuffer, int inNumBytes,
202
char inAllowedToBlock ) {
204
int *socketIDptr = (int *)( mNativeObjectPointer );
205
int socketID = socketIDptr[0];
207
if( inAllowedToBlock ) {
209
return ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
212
// 1 for non-blocking, 0 for blocking
213
u_long socketMode = 1;
214
ioctlsocket( socketID, FIONBIO, &socketMode );
216
int result = ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
218
// set back to blocking
220
ioctlsocket( socketID, FIONBIO, &socketMode );
224
WSAGetLastError() == WSAEWOULDBLOCK ) {
236
int Socket::receive( unsigned char *inBuffer, int inNumBytes,
239
int *socketIDptr = (int *)( mNativeObjectPointer );
240
int socketID = socketIDptr[0];
245
char errorReturnValue = -1;
248
char stopLooping = false;
251
// for win32, we can't specify MSG_WAITALL
252
// so we have too loop until the entire message is received,
253
// as long as there is no error.
255
// note that if a timeout is set, we use the stopLooping flag
256
// to return only the available data (we do not emulate MSG_WAITALL)
258
while( numReceived < inNumBytes &&
262
// the number of bytes left to receive
263
int numRemaining = inNumBytes - numReceived;
265
// pointer to the spot in the buffer where the
266
// remaining bytes should be stored
267
unsigned char *remainingBuffer = &( inBuffer[ numReceived ] );
271
if( inTimeout == -1 ) {
273
recv( socketID, (char*)remainingBuffer, numRemaining, 0 );
277
timed_read( socketID, remainingBuffer,
278
numRemaining, inTimeout );
280
// stop looping after one timed read
285
if( numReceivedIn > 0 ) {
286
numReceived += numReceivedIn;
291
if( numReceivedIn == 0 ) {
292
// the socket was gracefully closed
293
errorReturnValue = -1;
295
else if( numReceivedIn == SOCKET_ERROR ) {
297
errorReturnValue = -1;
299
else if( numReceivedIn == -2 ) {
301
errorReturnValue = -2;
304
printf( "Unexpected return value from socket receive: %d.\n",
306
errorReturnValue = -1;
314
return errorReturnValue;
323
void Socket::breakConnection() {
324
int *socketIDptr = (int *)( mNativeObjectPointer );
325
int socketID = socketIDptr[0];
327
if( !mIsConnectionBroken ) {
329
shutdown( socketID, 2 );
330
mIsConnectionBroken = true;
333
closesocket( socketID );
338
HostAddress *Socket::getRemoteHostAddress() {
340
int *socketIDptr = (int *)( mNativeObjectPointer );
341
int socketID = socketIDptr[0];
343
// adapted from Unix Socket FAQ
346
struct sockaddr_in sin;
349
int error = getpeername( socketID, (struct sockaddr *) &sin, &len );
355
// this is potentially insecure, since a fake DNS name might be returned
356
// we should use the IP address only
358
// struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr,
359
// sizeof sin.sin_addr,
363
NetworkFunctionLocks::mInet_ntoaLock.lock();
364
// returned string is statically allocated, copy it
365
char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) );
366
NetworkFunctionLocks::mInet_ntoaLock.unlock();
368
int port = ntohs( sin.sin_port );
370
return new HostAddress( ipAddress, port );
375
HostAddress *Socket::getLocalHostAddress() {
376
int *socketIDptr = (int *)( mNativeObjectPointer );
377
int socketID = socketIDptr[0];
379
// adapted from GTK-gnutalla code, and elsewhere
381
struct sockaddr_in addr;
382
int len = sizeof( struct sockaddr_in );
384
int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len );
391
char *stringAddress = inet_ntoa( addr.sin_addr );
393
return new HostAddress( stringDuplicate( stringAddress ),
401
/* timed_read adapted from gnut, by Josh Pieper */
402
/* Josh Pieper, (c) 2000 */
403
/* This file is distributed under the GPL, see file COPYING for details */
405
// exactly like the real read, except that it returns -2
406
// if no data was read before the timeout occurred...
407
int timed_read( int inSock, unsigned char *inBuf,
408
int inLen, long inMilliseconds ) {
414
FD_SET( inSock, &fsr );
416
tv.tv_sec = inMilliseconds / 1000;
417
int remainder = inMilliseconds % 1000;
418
tv.tv_usec = remainder * 1000;
420
ret = select( inSock + 1, &fsr, NULL, NULL, &tv );
423
// printf( "Timed out waiting for data on socket receive.\n" );
428
printf( "Selecting socket during receive failed.\n" );
432
ret = recv( inSock, (char*)inBuf, inLen, 0 );
436
// select came back as 1, but no data there
437
// connection closed on remote end