PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ClassMaster2014/barahon/Cultivation_9_UnixSource/minorGems/network/win32/SocketWin32.cpp

https://gitlab.com/garheade/linux_camp
C++ | 483 lines | 226 code | 131 blank | 126 comment | 43 complexity | c8f4655a0bb94f4a7313ae7828726bb4 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Modification History
  3. *
  4. * 2001-January-28 Jason Rohrer
  5. * Created.
  6. *
  7. * 2001-February-4 Jason Rohrer
  8. * Fixed receive so that it waits for all requested bytes to arrive.
  9. *
  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.
  13. *
  14. * 2001-May-12 Jason Rohrer
  15. * Fixed a bug in socket receive error checking.
  16. *
  17. * 2001-November-13 Jason Rohrer
  18. * Changed timeout parameter to signed, since -1 is a possible argument.
  19. *
  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?
  25. *
  26. * 2002-August-2 Jason Rohrer
  27. * Added functon for getting remote host address, but no implementation.
  28. *
  29. * 2002-August-5 Jason Rohrer
  30. * Added implementation of getRemoteHostAddress().
  31. *
  32. * 2002-September-8 Jason Rohrer
  33. * Fixed a major looping bug with broken sockets.
  34. *
  35. * 2002-November-15 Jason Rohrer
  36. * Fixed a security hole when getting the remote host address.
  37. *
  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.
  41. *
  42. * 2003-February-5 Jason Rohrer
  43. * Fixed a bug in call to gethostname. Removed unused variable.
  44. *
  45. * 2004-January-4 Jason Rohrer
  46. * Added use of network function locks.
  47. *
  48. * 2004-January-11 Jason Rohrer
  49. * Fixed a bug in handling of timeout return value.
  50. *
  51. * 2004-March-23 Jason Rohrer
  52. * Removed timeout error message.
  53. *
  54. * 2004-December-13 Jason Rohrer
  55. * Added a breakConnection function.
  56. *
  57. * 2004-December-24 Jason Rohrer
  58. * Fixed bug in close call.
  59. *
  60. * 2005-July-5 Jason Rohrer
  61. * Added port number when getting address of remote host.
  62. *
  63. * 2006-May-28 Jason Rohrer
  64. * Changed timeout behavior slightly to support emulation of non-blocking mode.
  65. *
  66. * 2006-June-5 Jason Rohrer
  67. * Added support for non-blocking sends.
  68. *
  69. * 2008-September-30 Jason Rohrer
  70. * Added support for non-blocking connect.
  71. * Fixed close-detection in non-blocking read.
  72. *
  73. * 2010-January-26 Jason Rohrer
  74. * Added support for disabling Nagle algorithm.
  75. */
  76. #include "minorGems/network/Socket.h"
  77. #include "minorGems/network/NetworkFunctionLocks.h"
  78. #include <winsock.h>
  79. #include <windows.h>
  80. #include <stdio.h>
  81. #include <time.h>
  82. #include <string.h>
  83. // prototypes
  84. int timed_read( int inSock, unsigned char *inBuf,
  85. int inLen, long inMilliseconds );
  86. /**
  87. * Windows-specific implementation of the Socket class member functions.
  88. *
  89. */
  90. // Win32 does not define socklen_t
  91. typedef int socklen_t;
  92. char Socket::sInitialized = false;
  93. int Socket::initSocketFramework() {
  94. WORD wVersionRequested;
  95. WSADATA wsaData;
  96. int err;
  97. wVersionRequested = MAKEWORD( 1, 0 );
  98. err = WSAStartup( wVersionRequested, &wsaData );
  99. if ( err != 0 ) {
  100. // no usable DLL found
  101. printf( "WinSock DLL version 1.0 or higher not found.\n" );
  102. return -1;
  103. }
  104. sInitialized = true;
  105. return 0;
  106. }
  107. Socket::~Socket() {
  108. int *socketIDptr = (int *)( mNativeObjectPointer );
  109. int socketID = socketIDptr[0];
  110. if( !mIsConnectionBroken ) {
  111. // 2 specifies shutting down both sends and receives
  112. shutdown( socketID, 2 );
  113. mIsConnectionBroken = true;
  114. }
  115. closesocket( socketID );
  116. delete [] socketIDptr;
  117. }
  118. int Socket::isConnected() {
  119. if( mConnected ) {
  120. return 1;
  121. }
  122. int *socketIDptr = (int *)( mNativeObjectPointer );
  123. int socketID = socketIDptr[0];
  124. int ret;
  125. fd_set fsr;
  126. struct timeval tv;
  127. int val;
  128. socklen_t len;
  129. FD_ZERO( &fsr );
  130. FD_SET( socketID, &fsr );
  131. // check if connection event waiting right now
  132. // timeout of 0
  133. tv.tv_sec = 0;
  134. tv.tv_usec = 0;
  135. ret = select( socketID + 1, NULL, &fsr, NULL, &tv );
  136. if( ret==0 ) {
  137. // timeout
  138. return 0;
  139. }
  140. // no timeout
  141. // error?
  142. len = 4;
  143. ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len );
  144. if( ret < 0 ) {
  145. // error
  146. return -1;
  147. }
  148. if( val != 0 ) {
  149. // error
  150. return -1;
  151. }
  152. // success
  153. mConnected = true;
  154. return 1;
  155. }
  156. void Socket::setNoDelay( int inValue ) {
  157. int *socketIDptr = (int *)( mNativeObjectPointer );
  158. int socketID = socketIDptr[0];
  159. int flag = inValue;
  160. int result = setsockopt( socketID,
  161. IPPROTO_TCP,
  162. TCP_NODELAY,
  163. (char *) &flag,
  164. sizeof(int) );
  165. }
  166. int Socket::send( unsigned char *inBuffer, int inNumBytes,
  167. char inAllowedToBlock,
  168. char inAllowDelay ) {
  169. int *socketIDptr = (int *)( mNativeObjectPointer );
  170. int socketID = socketIDptr[0];
  171. if( inAllowedToBlock ) {
  172. if( ! inAllowDelay ) {
  173. // turn nodelay on
  174. setNoDelay( 1 );
  175. }
  176. int returnVal = ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
  177. if( ! inAllowDelay ) {
  178. // turn nodelay back off
  179. setNoDelay( 0 );
  180. }
  181. return returnVal;
  182. }
  183. else {
  184. // 1 for non-blocking, 0 for blocking
  185. u_long socketMode = 1;
  186. ioctlsocket( socketID, FIONBIO, &socketMode );
  187. if( ! inAllowDelay ) {
  188. // turn nodelay on
  189. setNoDelay( 1 );
  190. }
  191. int result = ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
  192. if( ! inAllowDelay ) {
  193. // turn nodelay back off
  194. setNoDelay( 0 );
  195. }
  196. // set back to blocking
  197. socketMode = 0;
  198. ioctlsocket( socketID, FIONBIO, &socketMode );
  199. if( result == -1 &&
  200. WSAGetLastError() == WSAEWOULDBLOCK ) {
  201. return -2;
  202. }
  203. else {
  204. return result;
  205. }
  206. }
  207. }
  208. int Socket::receive( unsigned char *inBuffer, int inNumBytes,
  209. long inTimeout ) {
  210. int *socketIDptr = (int *)( mNativeObjectPointer );
  211. int socketID = socketIDptr[0];
  212. int numReceived = 0;
  213. char error = false;
  214. char errorReturnValue = -1;
  215. char stopLooping = false;
  216. // for win32, we can't specify MSG_WAITALL
  217. // so we have too loop until the entire message is received,
  218. // as long as there is no error.
  219. // note that if a timeout is set, we use the stopLooping flag
  220. // to return only the available data (we do not emulate MSG_WAITALL)
  221. while( numReceived < inNumBytes &&
  222. !error &&
  223. !stopLooping ) {
  224. // the number of bytes left to receive
  225. int numRemaining = inNumBytes - numReceived;
  226. // pointer to the spot in the buffer where the
  227. // remaining bytes should be stored
  228. unsigned char *remainingBuffer = &( inBuffer[ numReceived ] );
  229. int numReceivedIn;
  230. if( inTimeout == -1 ) {
  231. numReceivedIn =
  232. recv( socketID, (char*)remainingBuffer, numRemaining, 0 );
  233. }
  234. else {
  235. numReceivedIn =
  236. timed_read( socketID, remainingBuffer,
  237. numRemaining, inTimeout );
  238. // stop looping after one timed read
  239. stopLooping = true;
  240. }
  241. if( numReceivedIn > 0 ) {
  242. numReceived += numReceivedIn;
  243. }
  244. else {
  245. error = true;
  246. if( numReceivedIn == 0 ) {
  247. // the socket was gracefully closed
  248. errorReturnValue = -1;
  249. }
  250. else if( numReceivedIn == SOCKET_ERROR ) {
  251. // socket error
  252. errorReturnValue = -1;
  253. }
  254. else if( numReceivedIn == -2 ) {
  255. // timeout
  256. errorReturnValue = -2;
  257. }
  258. else {
  259. printf( "Unexpected return value from socket receive: %d.\n",
  260. numReceivedIn );
  261. errorReturnValue = -1;
  262. }
  263. }
  264. }
  265. if( error ) {
  266. return errorReturnValue;
  267. }
  268. else {
  269. return numReceived;
  270. }
  271. }
  272. void Socket::breakConnection() {
  273. int *socketIDptr = (int *)( mNativeObjectPointer );
  274. int socketID = socketIDptr[0];
  275. if( !mIsConnectionBroken ) {
  276. shutdown( socketID, 2 );
  277. mIsConnectionBroken = true;
  278. }
  279. closesocket( socketID );
  280. }
  281. HostAddress *Socket::getRemoteHostAddress() {
  282. int *socketIDptr = (int *)( mNativeObjectPointer );
  283. int socketID = socketIDptr[0];
  284. // adapted from Unix Socket FAQ
  285. socklen_t len;
  286. struct sockaddr_in sin;
  287. len = sizeof sin;
  288. int error = getpeername( socketID, (struct sockaddr *) &sin, &len );
  289. if( error ) {
  290. return NULL;
  291. }
  292. // this is potentially insecure, since a fake DNS name might be returned
  293. // we should use the IP address only
  294. //
  295. // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr,
  296. // sizeof sin.sin_addr,
  297. // AF_INET );
  298. NetworkFunctionLocks::mInet_ntoaLock.lock();
  299. // returned string is statically allocated, copy it
  300. char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) );
  301. NetworkFunctionLocks::mInet_ntoaLock.unlock();
  302. int port = ntohs( sin.sin_port );
  303. return new HostAddress( ipAddress, port );
  304. }
  305. HostAddress *Socket::getLocalHostAddress() {
  306. int *socketIDptr = (int *)( mNativeObjectPointer );
  307. int socketID = socketIDptr[0];
  308. // adapted from GTK-gnutalla code, and elsewhere
  309. struct sockaddr_in addr;
  310. int len = sizeof( struct sockaddr_in );
  311. int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len );
  312. if( result == -1 ) {
  313. return NULL;
  314. }
  315. else {
  316. char *stringAddress = inet_ntoa( addr.sin_addr );
  317. return new HostAddress( stringDuplicate( stringAddress ),
  318. 0 );
  319. }
  320. }
  321. /* timed_read adapted from gnut, by Josh Pieper */
  322. /* Josh Pieper, (c) 2000 */
  323. /* This file is distributed under the GPL, see file COPYING for details */
  324. // exactly like the real read, except that it returns -2
  325. // if no data was read before the timeout occurred...
  326. int timed_read( int inSock, unsigned char *inBuf,
  327. int inLen, long inMilliseconds ) {
  328. fd_set fsr;
  329. struct timeval tv;
  330. int ret;
  331. FD_ZERO( &fsr );
  332. FD_SET( inSock, &fsr );
  333. tv.tv_sec = inMilliseconds / 1000;
  334. int remainder = inMilliseconds % 1000;
  335. tv.tv_usec = remainder * 1000;
  336. ret = select( inSock + 1, &fsr, NULL, NULL, &tv );
  337. if( ret==0 ) {
  338. // printf( "Timed out waiting for data on socket receive.\n" );
  339. return -2;
  340. }
  341. if( ret<0 ) {
  342. printf( "Selecting socket during receive failed.\n" );
  343. return ret;
  344. }
  345. ret = recv( inSock, (char*)inBuf, inLen, 0 );
  346. if( ret == 0 ) {
  347. // select came back as 1, but no data there
  348. // connection closed on remote end
  349. return -1;
  350. }
  351. return ret;
  352. }