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

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

#1 get address from local socket or peer socket

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