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

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

#1 fix socket patch for windows

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