source: libcf++/trunk/src/network.cpp@ 9

Last change on this file since 9 was 9, checked in by cheese, 9 years ago

#1 fix binding interface

File size: 11.5 KB
Line 
1/**
2 * @file network.cpp
3 * @author myusgun@gmail.com
4 * @brief network
5 */
6#include "cf/network.h"
7
8#include <iostream>
9#include <sstream>
10#include <string.h>
11
12#ifdef _ON_WINDOWS
13# include <WinSock2.h>
14# pragma comment(lib, "ws2_32.lib")
15#else
16# include <netinet/in.h>
17# include <sys/socket.h>
18# include <arpa/inet.h>
19# include <netdb.h>
20# include <unistd.h>
21# include <sys/un.h>
22# include <fcntl.h>
23# include <errno.h>
24# include <sys/types.h>
25# include <sys/time.h>
26#endif
27
28#ifdef _ON_WINDOWS
29typedef cf::int32_t socklen_t;
30# define sa_family_t cf::uint16_t
31# define ERROR_INTR WSAEINTR
32# define ERROR_CONNECTING WSAEWOULDBLOCK
33# define SOCKET_API_CALL __stdcall
34#else
35# define closesocket(__sock) ::close(__sock)
36# define ERROR_INTR EINTR
37# define ERROR_CONNECTING EINPROGRESS
38# define SOCKET_API_CALL
39#endif
40
41/*--------------------------------------------------------------*/
42/**
43 * do not include "cf/macaddr.[ch]pp"
44 * just declare and call
45 */
46long GetMACAddress(cf::char_t *addr);
47
48static std::string convertAddressToString(struct sockaddr_in & addr)
49{
50 cf::uint8_t * bytes = NULL;
51 cf::char_t str[32] = {0x00};
52
53 bytes = reinterpret_cast<cf::uint8_t *>(&addr.sin_addr.s_addr);
54 snprintf(str, sizeof(str) - 1, "%u.%u.%u.%u", bytes[0],
55 bytes[1],
56 bytes[2],
57 bytes[3]);
58
59 return str;
60}
61
62static cf::void_t waitForTimeout(const cf::socket_t sock,
63 const cf::int32_t timeout,
64 const cf::bool_t checkWriteFD)
65 throw (cf::exception)
66{
67 cf::int32_t result = 0;
68 fd_set rfds;
69 fd_set wfds;
70 fd_set * wfdsPtr = checkWriteFD ? &wfds : NULL;
71
72 struct timeval tv;
73
74 if (timeout <= 0)
75 return;
76
77 FD_ZERO(&rfds);
78 FD_SET(sock, &rfds);
79
80 FD_ZERO(&wfds);
81 FD_SET(sock, &wfds);
82
83 tv.tv_sec = timeout;
84 tv.tv_usec = 0;
85
86 result = select(sock + 1, &rfds, wfdsPtr, NULL, &tv);
87 if (result < 0)
88 THROW_EXCEPTION("select error");
89 else if (result == 0)
90 THROW_EXCEPTION("socket timed out");
91
92 if (!FD_ISSET(sock, &rfds))
93 {
94 if (!checkWriteFD)
95 THROW_EXCEPTION("read fd is not set");
96 else if (!FD_ISSET(sock, &wfds))
97 THROW_EXCEPTION("write fd is not set");
98 }
99}
100
101static cf::void_t setReuseAddress(const cf::network::tcp & tcp)
102 throw (cf::exception)
103{
104 try
105 {
106 cf::int32_t reuseaddr = 1;
107
108 tcp.setOption(SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
109 }
110 catch (cf::exception & e)
111 {
112 FORWARD_EXCEPTION(e);
113 }
114}
115
116typedef cf::int32_t (SOCKET_API_CALL * getSocketNameAPI)(cf::socket_t sock,
117 struct sockaddr * addr,
118 socklen_t *len);
119
120static cf::network::host getSocketNameFromFunction(const cf::socket_t sock,
121 getSocketNameAPI api)
122 throw (cf::exception)
123{
124 cf::int32_t result = 0;
125 struct sockaddr_in addr;
126 socklen_t len = sizeof(struct sockaddr_in);
127
128 result = api(sock, (struct sockaddr *)&addr, &len);
129 if (result < 0)
130 THROW_EXCEPTION("cannot get sockket or peer name ("
131 << cf::exception::systemCode()
132 << ")");
133
134 return cf::network::host(convertAddressToString(addr), addr.sin_port);
135}
136/*--------------------------------------------------------------*/
137
138cf::network::host::host(const std::string & address,
139 const cf::uint16_t port)
140 : mAddress(address),
141 mPort(port)
142{
143}
144
145std::string cf::network::host::address() const
146{
147 return mAddress;
148}
149
150cf::uint16_t cf::network::host::port() const
151{
152 return mPort;
153}
154
155cf::bool_t cf::network::host::empty() const
156{
157 return (address().empty() || port() <= 0) ? true : false;
158}
159
160cf::network::tcp::tcp(const cf::socket_t attachedSocket)
161 throw (cf::exception)
162 : mSocket (attachedSocket),
163 mTimeout(0)
164{
165 static cf::bool_t isInitialized = false;
166
167 /* initialize socket */
168 if (!isInitialized)
169 {
170#if defined(_WIN32) || defined(_WIN64)
171 WSADATA winsockData;
172 cf::int32_t result = WSAStartup(MAKEWORD(2, 0), &winsockData);
173 if (result)
174 THROW_EXCEPTION("cannot start-up winsock");
175#endif
176
177 isInitialized = true;
178 }
179
180 if (mSocket == UNUSED_SOCKET)
181 mSocket = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
182
183 if (mSocket < 0)
184 THROW_EXCEPTION("cannot create a socket");
185}
186
187cf::network::tcp::~tcp()
188{
189 close();
190}
191
192cf::socket_t cf::network::tcp::descriptor() const
193{
194 return mSocket;
195}
196
197cf::void_t cf::network::tcp::close()
198{
199 if (mSocket == UNUSED_SOCKET)
200 return;
201
202 closesocket(mSocket);
203
204 mSocket = UNUSED_SOCKET;
205}
206
207cf::void_t cf::network::tcp::connect(const cf::network::host & peer,
208 const cf::int32_t timeout)
209 throw (cf::exception)
210{
211 if (peer.empty())
212 THROW_EXCEPTION("invalid host info");
213
214 cf::int32_t result = 0;
215 cf::int32_t retval = 0;
216 cf::int32_t length = 0;
217 struct sockaddr_in addr;
218 struct hostent * hostEnt;
219
220 const cf::char_t * host = peer.address().c_str();
221 cf::uint16_t port = peer.port();
222
223 /* 1. set data */
224 addr.sin_family = AF_INET;
225 addr.sin_port = htons(port);
226 addr.sin_addr.s_addr = inet_addr(host);
227
228 /* 2. get ip from hostname if inet_addr() is failed */
229 if (addr.sin_addr.s_addr == (unsigned int)-1)
230 {
231 hostEnt = gethostbyname(host);
232 if (hostEnt == NULL)
233 THROW_EXCEPTION("cannot get host by name");
234
235 addr.sin_family = (sa_family_t)hostEnt->h_addrtype;
236 memcpy(&(addr.sin_addr.s_addr),
237 hostEnt->h_addr,
238 (size_t)hostEnt->h_length);
239 }
240
241 /* 3. set options */
242 try
243 {
244 setReuseAddress(*this);
245 }
246 catch (cf::exception & e)
247 {
248 FORWARD_EXCEPTION(e);
249 }
250
251 setTimeout(timeout);
252
253 /* 4. connect */
254 result = ::connect(mSocket, (struct sockaddr *)&addr, sizeof(addr));
255 if (result < 0)
256 {
257 if (timeout > 0)
258 {
259 if (cf::exception::systemCode() != ERROR_CONNECTING)
260 THROW_EXCEPTION("socket connect error");
261
262 try
263 {
264 length = sizeof(retval);
265 waitForTimeout(mSocket, timeout, true);
266 getOption(SO_ERROR, &retval, &length);
267 }
268 catch (cf::exception & e)
269 {
270 FORWARD_EXCEPTION(e);
271 }
272
273 if (retval)
274 THROW_EXCEPTION("cannot get error from socket option");
275 }
276 else
277 {
278 THROW_EXCEPTION("cannot connect to " << host << ":" << port);
279 }
280 }
281}
282
283cf::void_t cf::network::tcp::bind(const cf::uint16_t port) const
284 throw (cf::exception)
285{
286 if (!port)
287 THROW_EXCEPTION("invalid port number");
288
289 cf::int32_t result = 0;
290 struct sockaddr_in addr;
291
292 /* 1. set data */
293 addr.sin_family = AF_INET;
294 addr.sin_addr.s_addr = htonl(INADDR_ANY);
295 addr.sin_port = htons(port);
296
297 /* 2. set options */
298 try
299 {
300 setReuseAddress(*this);
301 }
302 catch (cf::exception & e)
303 {
304 FORWARD_EXCEPTION(e);
305 }
306
307 result = ::bind(mSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr));
308 if (result < 0)
309 THROW_EXCEPTION("cannot bind to " << port);
310}
311
312cf::void_t cf::network::tcp::listen(const cf::int32_t backlog) const
313 throw (cf::exception)
314{
315 cf::int32_t result = ::listen(mSocket, backlog);
316 if (result < 0)
317 THROW_EXCEPTION("cannot listen");
318}
319
320cf::network::tcp cf::network::tcp::accept() const
321 throw (cf::exception)
322{
323 cf::socket_t sock = 0;
324 struct sockaddr_in addr;
325 socklen_t len = sizeof(addr);
326
327 sock = ::accept(mSocket, (struct sockaddr *)&addr, /* in/out */&len);
328 if (sock < 0)
329 THROW_EXCEPTION("cannot accept client");
330
331 return cf::network::tcp(sock).detach();
332}
333
334cf::void_t cf::network::tcp::attach(const cf::socket_t sock)
335 throw (exception)
336{
337 if (sock == UNUSED_SOCKET)
338 THROW_EXCEPTION("has invalid socket");
339
340 mSocket = sock;
341}
342
343cf::socket_t cf::network::tcp::detach()
344 throw (exception)
345{
346 if (mSocket == UNUSED_SOCKET)
347 THROW_EXCEPTION("has invalid socket");
348
349 cf::socket_t sock = mSocket;
350
351 mSocket = UNUSED_SOCKET;
352
353 return sock;
354}
355
356cf::void_t cf::network::tcp::send(const cf::bin & in) const
357 throw (cf::exception)
358{
359 if (in.empty())
360 THROW_EXCEPTION("send data is null or zero-bytes");
361
362 cf::char_t * buf = reinterpret_cast<cf::char_t *>(in.buffer());
363 cf::int32_t size = static_cast<cf::int32_t>(in.size());
364
365 cf::int32_t sentSize = (cf::int32_t)::send (mSocket, buf, size, 0);
366 if (sentSize != size)
367 THROW_EXCEPTION("cannot send (" << cf::exception::systemCode() << ")");
368}
369
370cf::void_t cf::network::tcp::receive(cf::bin & out) const
371 throw (cf::exception)
372{
373 if (out.size() == 0)
374 THROW_EXCEPTION("binary buffer is not created");
375
376 try
377 {
378 waitForTimeout(mSocket, mTimeout, false);
379 }
380 catch (cf::exception & e)
381 {
382 FORWARD_EXCEPTION(e);
383 }
384
385 cf::char_t * buf = reinterpret_cast<cf::char_t *>(out.buffer());
386 cf::int32_t size = static_cast<cf::int32_t>(out.size());
387
388 cf::int32_t receivedSize = (cf::int32_t)::recv(mSocket, buf, size, 0);
389 if (receivedSize < 0)
390 THROW_EXCEPTION("cannot receive (" << cf::exception::systemCode() << ")");
391 else if (receivedSize == 0)
392 out.clear();
393 else if (receivedSize < size)
394 out.resize(receivedSize);
395}
396
397cf::bin cf::network::tcp::receive(const cf::int32_t size) const
398 throw (cf::exception)
399{
400 cf::bin out;
401
402 out.resize(size);
403 receive(out);
404
405 return out;
406}
407
408cf::bin cf::network::tcp::receive() const
409 throw (cf::exception)
410{
411 const cf::size_t bufferSize = 1024;
412
413 cf::bin buffer;
414 cf::bin out;
415
416 buffer.resize(bufferSize);
417
418 do
419 {
420 receive(buffer);
421 out.append(buffer);
422 } while (buffer.size() == bufferSize);
423
424 return out;
425}
426
427cf::void_t cf::network::tcp::getOption(const cf::int32_t optname,
428 cf::void_t * optval,
429 cf::int32_t * optlen) const
430 throw (cf::exception)
431{
432 cf::int32_t result = getsockopt(mSocket,
433 SOL_SOCKET,
434 optname,
435#ifdef _ON_WINDOWS
436 (cf::char_t *)optval,
437#else
438 optval,
439#endif
440 (socklen_t *)optlen);
441 if (result < 0)
442 THROW_EXCEPTION("cannot get socket option ("
443 << cf::exception::systemCode()
444 << ")");
445}
446
447cf::void_t cf::network::tcp::setOption(const cf::int32_t optname,
448 const cf::void_t * optval,
449 const cf::int32_t optlen) const
450 throw (cf::exception)
451{
452 cf::int32_t result = setsockopt(mSocket,
453 SOL_SOCKET,
454 optname,
455#ifdef _ON_WINDOWS
456 (cf::char_t *)optval,
457#else
458 optval,
459#endif
460 (socklen_t)optlen);
461 if (result < 0)
462 THROW_EXCEPTION("cannot set socket option ("
463 << cf::exception::systemCode()
464 << ")");
465}
466
467cf::void_t cf::network::tcp::setNonBlocking(const cf::bool_t flag)
468{
469#ifdef _ON_WINDOWS
470 cf::ulong_t mode = flag ? 1 : 0;
471 ioctlsocket(mSocket, FIONBIO, &mode);
472#else
473 cf::int32_t mode = fcntl(mSocket, F_GETFL, 0);
474
475 if (flag) mode |= O_NONBLOCK;
476 else mode &= ~O_NONBLOCK;
477
478 fcntl(mSocket, F_SETFL, mode);
479#endif
480}
481
482cf::void_t cf::network::tcp::setTimeout(const cf::int32_t seconds)
483{
484 setNonBlocking(seconds > 0 /*? true : false*/);
485 mTimeout = seconds;
486}
487
488cf::network::host cf::network::tcp::peer() const
489 throw (cf::exception)
490{
491 try
492 {
493 return getSocketNameFromFunction(mSocket, getpeername);
494 }
495 catch (cf::exception & e)
496 {
497 FORWARD_EXCEPTION(e);
498 }
499}
500
501cf::network::host cf::network::tcp::local() const
502 throw (cf::exception)
503{
504 try
505 {
506 return getSocketNameFromFunction(mSocket, getsockname);
507 }
508 catch (cf::exception & e)
509 {
510 FORWARD_EXCEPTION(e);
511 }
512}
513
514std::string cf::network::nic::getMACAddress()
515 throw (cf::exception)
516{
517 static const cf::int32_t macSize = 6; /* = 48-bits */
518 static cf::bool_t isDone = false;
519 static cf::uint8_t bytes[macSize] = {0x00,};
520 static cf::char_t asciiz[macSize * 2 + 1] = {0x00,};
521
522 if (!isDone)
523 {
524 long result = 0;
525
526 result = GetMACAddress(reinterpret_cast<cf::char_t *>(bytes));
527 if (result)
528 THROW_EXCEPTION("cannot get mac-address");
529
530 for (cf::int32_t iter = 0 ; iter < macSize ; iter++)
531 snprintf(asciiz + (iter * 2), sizeof(asciiz) - 1,
532 "%02x",
533 bytes[iter]);
534
535 isDone = true;
536 }
537
538 return std::string(asciiz);
539}
540
541
542cf::uint32_t cf::network::byteOrder::htonl(cf::uint32_t in)
543{
544 return ::htonl(in);
545}
546
547cf::uint16_t cf::network::byteOrder::htons(cf::uint16_t in)
548{
549 return ::htons(in);
550}
551
552cf::uint32_t cf::network::byteOrder::ntohl(cf::uint32_t in)
553{
554 return ::ntohl(in);
555}
556
557cf::uint16_t cf::network::byteOrder::ntohs(cf::uint16_t in)
558{
559 return ::ntohs(in);
560}
Note: See TracBrowser for help on using the repository browser.