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

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

#1 commit prototype

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