source: libcf++/trunk/src/net.cpp@ 23

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

#1 socket instance 소멸 시에 close 안하도록 수정

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