PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/r3/socket.cpp

https://github.com/bcrowell/planetfinder_ios
C++ | 383 lines | 281 code | 49 blank | 53 comment | 58 complexity | 2e0b0c63b9ed85d737f958ef7c804c87 MD5 | raw file
  1. /*
  2. * socket
  3. *
  4. */
  5. /*
  6. Copyright (c) 2008-2010 Cass Everitt
  7. All rights reserved.
  8. Redistribution and use in source and binary forms, with or
  9. without modification, are permitted provided that the following
  10. conditions are met:
  11. * Redistributions of source code must retain the above
  12. copyright notice, this list of conditions and the following
  13. disclaimer.
  14. * Redistributions in binary form must reproduce the above
  15. copyright notice, this list of conditions and the following
  16. disclaimer in the documentation and/or other materials
  17. provided with the distribution.
  18. * The names of contributors to this software may not be used
  19. to endorse or promote products derived from this software
  20. without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  29. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. POSSIBILITY OF SUCH DAMAGE.
  33. Cass Everitt
  34. */
  35. #include "r3/socket.h"
  36. #include "r3/output.h"
  37. #ifndef _WIN32
  38. # define SOCKET int
  39. # include <unistd.h>
  40. # include <sys/socket.h>
  41. # include <netinet/in.h>
  42. # include <netinet/tcp.h>
  43. # include <arpa/inet.h>
  44. # include <netdb.h>
  45. #else
  46. # include <io.h>
  47. # include <winsock2.h>
  48. # include <windows.h>
  49. #endif
  50. #include <errno.h>
  51. #include <fcntl.h>
  52. #include <assert.h>
  53. #include <stdio.h>
  54. using namespace std;
  55. using namespace r3;
  56. #ifdef _WIN32
  57. #pragma comment( lib, "ws2_32" )
  58. namespace {
  59. void InitWindowsSockets() {
  60. static bool initialized = false;
  61. if( ! initialized ) {
  62. WSADATA wsadata;
  63. if( int err = WSAStartup( MAKEWORD(2,0), &wsadata ) ) {
  64. Output( "Could not initialize Windows sockets." );
  65. }
  66. initialized = true;
  67. }
  68. }
  69. }
  70. #define INIT_SOCKET_LIB() InitWindowsSockets()
  71. #define GET_ERROR() WSAGetLastError()
  72. #else
  73. #define INIT_SOCKET_LIB()
  74. #define GET_ERROR() errno
  75. #define SOCKET_ERROR -1
  76. #define INVALID_SOCKET -1
  77. #endif
  78. #define SOCKADDR sockaddr
  79. namespace r3 {
  80. uint GetIpAddress( const string & hostname ) {
  81. INIT_SOCKET_LIB();
  82. hostent *hp;
  83. uint addr;
  84. if(hostname.size() == 0) {
  85. return 0;
  86. }
  87. addr = inet_addr( hostname.c_str() );
  88. if( addr != -1 ) {
  89. return addr;
  90. }
  91. hp = gethostbyname( hostname.c_str() );
  92. if( hp ) {
  93. addr = *(uint *) hp->h_addr;
  94. return addr;
  95. }
  96. return -1;
  97. }
  98. bool Socket::Connect( uint host, int port ) {
  99. INIT_SOCKET_LIB();
  100. sockaddr_in addr;
  101. int one = 1;
  102. addr.sin_family = AF_INET;
  103. addr.sin_port = htons(port);
  104. addr.sin_addr.s_addr = host;
  105. s = socket(AF_INET, SOCK_STREAM, 0);
  106. if( s < 0 ) {
  107. Output( "net::Socket::connect() call to ::socket() failed" );
  108. s = -1;
  109. return false;
  110. }
  111. if( connect( s, (sockaddr *) & addr, sizeof(addr) ) < 0 ) {
  112. Output( "net::Socket::connect() call to ::connect() failed" );
  113. perror("connect error");
  114. s = -1;
  115. return false;
  116. }
  117. if( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, (char *) & one, sizeof(one) ) < 0 ) {
  118. Output( "net::Socket::connect() call to ::setsockopt() failed" );
  119. Disconnect();
  120. perror("setsockopt error");
  121. s = -1;
  122. return false;
  123. }
  124. /*
  125. int sz = 1<<20;
  126. setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&sz, sizeof(sz));
  127. sz = 1<<18;
  128. setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&sz, sizeof(sz));
  129. */
  130. return true;
  131. }
  132. void Socket::Disconnect() {
  133. if( s < 0 )
  134. return;
  135. #ifdef _WIN32
  136. closesocket( s );
  137. #else
  138. close((int)s);
  139. #endif
  140. s = -1;
  141. }
  142. bool Socket::SetNonblocking() {
  143. #ifdef _WIN32
  144. u_long one = 1;
  145. if( ioctlsocket(s, FIONBIO, & one) ) {
  146. Output( "net::Socket::SetNonblocking: ioctlsocket" );
  147. return false;
  148. }
  149. #else
  150. if( fcntl( s, F_SETFL, O_NONBLOCK ) < 0 ) {
  151. Output( "net::Socket::set_nonblocking: fcntl" );
  152. return false;
  153. }
  154. #endif
  155. return true;
  156. }
  157. bool Socket::Read( char * dst, uint dst_bytes ) {
  158. //Output( "Socket::read() %d bytes to 0x%x", dst_bytes, dst);
  159. int n = dst_bytes;
  160. while( n > 0 ) {
  161. int i = recv( s, dst, n, 0 );
  162. if( i < 0 ) {
  163. #ifdef _WIN32
  164. int wsaerr = GET_ERROR();
  165. if( wsaerr == 10035 ) {
  166. Sleep(1); // no data, recv would block
  167. continue; // wait a msec and try again
  168. }
  169. return false;
  170. #else
  171. if( /*errno == EWOULDBLOCK ||*/ errno == EAGAIN ) {
  172. i=0;
  173. } else {
  174. Output( "net::Socket::read: recv" );
  175. return false;
  176. }
  177. #endif
  178. } else if ( i == 0 ) {
  179. Output( "Socket::read(): connection closed on remote end\n");
  180. return false;
  181. }
  182. //Output( "i=%d, n=%d",i,n );
  183. dst += i;
  184. n -= i;
  185. }
  186. //fprintf(stderr,"Done\n");
  187. return true;
  188. }
  189. int Socket::ReadPartial( char * dst, uint dst_bytes ) {
  190. //fprintf(stderr,"net::Socket::read() %d bytes to 0x%x\n", dst_bytes, dst);
  191. int i = recv( s, dst, dst_bytes, 0 );
  192. if( i < 0 ) {
  193. #ifdef _WIN32
  194. int wsaerr = GET_ERROR();
  195. if( wsaerr == 10035 ) {
  196. i=0;
  197. }
  198. #else
  199. if( errno == EWOULDBLOCK || errno == EAGAIN ) {
  200. i=0;
  201. } else {
  202. perror("net::Socket::read: recv");
  203. }
  204. #endif
  205. } else if ( i == 0 ) {
  206. Output( "r3::Socket::Read(): connection closed on remote end" );
  207. Disconnect();
  208. i=-1; // we'll use negative return to mean something bad happened...
  209. }
  210. return i;
  211. }
  212. bool Socket::CanRead() {
  213. fd_set fds;
  214. FD_ZERO( & fds );
  215. FD_SET( s, & fds );
  216. timeval tv;
  217. tv.tv_sec = 0;
  218. tv.tv_usec = 0;
  219. int r = 0;
  220. if( (r = select( (int)s+1, & fds, NULL, NULL, &tv )) <= 0 ) {
  221. if( r < 0 ) {
  222. Output( "r3::Socket::CanRead: select error" );
  223. Disconnect();
  224. }
  225. return false;
  226. }
  227. return true;
  228. }
  229. bool Socket::Write( const char * src, uint src_bytes ) {
  230. //Output( "r3::Socket::Write() %d bytes from 0x%x\n", src_bytes, src );
  231. int i=0;
  232. while( i < (int)src_bytes ) {
  233. int j = send( s, src+i, src_bytes-i, 0);
  234. if( j < 0 ) {
  235. if( /* errno == EWOULDBLOCK || */ errno == EAGAIN ) {
  236. fd_set fds;
  237. FD_ZERO( & fds );
  238. FD_SET( s, & fds );
  239. if( select( (int)s+1, NULL, & fds, NULL, NULL ) <= 0 ) {
  240. Output( "r3::Socket::Write: select error" );
  241. return false;
  242. }
  243. j=0;
  244. } else {
  245. Output( "r3::Socket::Write: write failed" );
  246. return false;
  247. }
  248. } else if( j == 0 ) {
  249. Output( "r3::Socket::Write() failed" );
  250. return false;
  251. }
  252. //fprintf(stderr,"j=%d, i=%d\n",j,i);
  253. i += j;
  254. }
  255. return true;
  256. }
  257. void Listener::Close() {
  258. if( s < 0 )
  259. return;
  260. #ifdef _WIN32
  261. closesocket( s );
  262. #else
  263. close((int)s);
  264. #endif
  265. s = -1;
  266. }
  267. bool Listener::SetNonblocking() {
  268. #ifdef _WIN32
  269. u_long one = 1;
  270. if( ioctlsocket(s, FIONBIO, & one) ) {
  271. Output( "r3::Listener::SetNonblocking: ioctlsocket failed" );
  272. return false;
  273. }
  274. #else
  275. if( fcntl( s, F_SETFL, O_NONBLOCK ) < 0 ) {
  276. Output( "r3::Listener::SetNonblocking: fcntl failed");
  277. return false;
  278. }
  279. #endif
  280. return true;
  281. }
  282. bool Listener::Listen( int port ) {
  283. INIT_SOCKET_LIB();
  284. s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  285. if (s == INVALID_SOCKET) {
  286. Output( "Error at socket(): %ld\n", GET_ERROR() );
  287. Close();
  288. return false;
  289. }
  290. sockaddr_in service;
  291. service.sin_family = AF_INET;
  292. service.sin_addr.s_addr = INADDR_ANY;
  293. service.sin_port = htons(port);
  294. if ( bind( s, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR ) {
  295. Output( "bind() failed." );
  296. Close();
  297. return false;
  298. }
  299. if ( listen( s, 1 ) == SOCKET_ERROR ) {
  300. Output( "Error listening on socket." );
  301. Close();
  302. return false;
  303. }
  304. SetNonblocking();
  305. return true;
  306. }
  307. Socket Listener::Accept() {
  308. if( s == -1 ) {
  309. return -1;
  310. }
  311. SOCKET client = accept( s, NULL, NULL );
  312. if ( client == SOCKET_ERROR ) {
  313. #ifdef _WIN32
  314. int wsaerr = GET_ERROR();
  315. if( wsaerr != WSAEWOULDBLOCK ) {
  316. Close();
  317. }
  318. #else
  319. #endif
  320. return Socket(-1);
  321. }
  322. return Socket( client );
  323. }
  324. void Listener::StopListening() {
  325. Close();
  326. }
  327. }