source: libcf/trunk/src/cf_socket.c@ 66

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

#1 add codec module (bin <-> hex-string)

File size: 11.4 KB
Line 
1/**
2 * @file cf_socket.c
3 * @author myusgun <myusgun@gmail.com>
4 */
5#include "cf_socket.h"
6#include "cf_local.h"
7#include "cf_error.h"
8
9#include <stdio.h>
10#include <string.h>
11
12#if defined(_WIN32) || defined(_WIN64)
13/* for windows {{{ */
14# include <WinSock2.h>
15# pragma comment (lib, "ws2_32.lib")
16/* }}} for windows */
17#else
18/* for linux/unix {{{ */
19# include <netinet/in.h>
20# include <sys/socket.h>
21# include <arpa/inet.h>
22# include <netdb.h>
23# include <unistd.h>
24# include <sys/un.h>
25# include <fcntl.h>
26# include <errno.h>
27/* }}} for linux/unix */
28#endif
29
30#define ASSERT_INIT() \
31 if (!CF_Socket_IsInitialized ()) \
32 return CF_ERROR_SOCKET_NOT_INITIALIZED
33
34#define ASSERT_SOCKET(__sock) \
35 if (__sock < 0) \
36 return CF_ERROR_SOCKET_INVALID_SOCKET
37
38#if defined(_WIN32) || defined(_WIN64)
39typedef int socklen_t;
40# define sa_family_t unsigned short
41# define close(__sock) closesocket(__sock)
42# define CHECK_SELECT(x) ((x) == SOCKET_ERROR)
43# define GET_SYSTEM_ERROR() WSAGetLastError ()
44# define ERROR_INTR WSAEINTR
45#else
46# define CHECK_SELECT(x) ((x) < 0)
47# define GET_SYSTEM_ERROR() errno
48# define ERROR_INTR EINTR
49#endif
50
51static CF_BOOL gInitialized = CF_FALSE;
52
53static void
54CF_Socket_Local_SetNonBlocking (const int sock,
55 CF_BOOL boolean)
56{
57#if defined(_WIN32) || defined(_WIN64)
58 unsigned long mode = (boolean == CF_TRUE) ? 1 : 0;
59 ioctlsocket (sock, FIONBIO, &mode);
60#else
61 int flags = fcntl (sock, F_GETFL, 0);
62
63 if (boolean)
64 flags |= O_NONBLOCK;
65 else
66 flags &= ~O_NONBLOCK;
67
68 fcntl (sock, F_SETFL, flags);
69#endif
70}
71
72static int
73CF_Socket_Local_SetLinger (const int sock)
74{
75 struct linger linger;
76
77 linger.l_onoff = 1;
78 linger.l_linger = 0;
79
80 return CF_Socket_SetOption (sock,
81 SO_LINGER,
82 &linger,
83 sizeof (linger));
84}
85
86static int
87CF_Socket_Local_SetReUseAddr (const int sock)
88{
89 int reuseaddr = 1;
90
91 return CF_Socket_SetOption (sock,
92 SO_REUSEADDR,
93 &reuseaddr,
94 sizeof (reuseaddr));
95}
96
97static int
98CF_Socket_Local_CheckTimeout (const int sock,
99 const int timeout)
100{
101 int result = 0;
102 fd_set readfds;
103 struct timeval tv;
104 int error;
105
106 if (!(timeout > CF_SOCKET_NO_TIMEOUT))
107 return CF_OK;
108
109 tv.tv_sec = timeout;
110 tv.tv_usec = 0;
111
112 FD_ZERO (&readfds);
113 FD_SET (sock, &readfds);
114
115 while (CHECK_SELECT (result = select (sock + 1, &readfds, NULL, NULL, &tv)))
116 {
117 error = GET_SYSTEM_ERROR ();
118 if (error != ERROR_INTR)
119 {
120 result = CF_ERROR_SOCKET_TIMEOUT;
121 return result;
122 }
123 }
124
125 if (FD_ISSET (sock, &readfds) == 0)
126 {
127 return CF_ERROR_SOCKET_INTERNAL;
128 }
129
130 return CF_OK;
131}
132
133/**
134 * 소켓의 초기화 상태 확인
135 *
136 * @return 초기화 된 경우, CF_TRUE; 그렇지 않은 경우, CF_FALSE
137 */
138CF_BOOL
139CF_Socket_IsInitialized (void)
140{
141 return gInitialized;
142}
143
144/**
145 * 소켓 초기화
146 *
147 * @return 성공 시, CF_OK; 실패 시, 오류 코드
148 */
149int
150CF_Socket_Initialize (void)
151{
152#if defined(_WIN32) || defined(_WIN64)
153 WSADATA winsockData;
154 if (WSAStartup (MAKEWORD (2, 0), &winsockData))
155 return CF_ERROR_SOCKET_INITIALIZE;
156#endif
157 gInitialized = CF_TRUE;
158
159 return CF_OK;
160}
161
162/**
163 * 소켓 해제
164 *
165 * @return 성공 시, CF_OK; 실패 시, 오류 코드
166 */
167int
168CF_Socket_Finalize (void)
169{
170 ASSERT_INIT ();
171
172#if defined(_WIN32) || defined(_WIN64)
173 if (WSACleanup ())
174 return CF_ERROR_SOCKET_FINALIZE;
175#endif
176
177 return CF_OK;
178}
179
180/**
181 * 소켓 닫기
182 *
183 * @return 성공 시, CF_OK; 실패 시, 오류 코드
184 *
185 * @param sock 소켓
186 */
187int
188CF_Socket_Close (const int sock)
189{
190 int result = 0;
191
192 ASSERT_SOCKET (sock);
193
194 result = close (sock);
195
196 if (result != 0)
197 return CF_ERROR_SOCKET_CLOSE;
198
199 return CF_OK;
200}
201
202/**
203 * 소켓 옵션 설정
204 *
205 * @return 성공 시 CF_OK; 실패 시, 오류 코드
206 *
207 * @param sock 소켓
208 * @param optname 옵션 이름
209 * @param optval 설정할 옵션 값의 메모리
210 * @param optlen 설정할 옵션의 길이
211 */
212int
213CF_Socket_SetOption (const int sock,
214 const int optname,
215 const void * optval,
216 const size_t optlen)
217{
218 int result = 0;
219
220 ASSERT_SOCKET (sock);
221
222 result = setsockopt (sock,
223 SOL_SOCKET,
224 optname,
225#if defined(_WIN32) || defined(_WIN64)
226 (char *) optval,
227#else
228 optval,
229#endif
230 (socklen_t) optlen);
231 if (result < 0)
232 return CF_ERROR_SOCKET_SET_OPTION;
233
234 return CF_OK;
235}
236
237/**
238 * 소켓 옵션 얻기
239 *
240 * @return 성공 시 CF_OK; 실패 시, 오류 코드
241 *
242 * @param sock 소켓
243 * @param optname 옵션 이름
244 * @param optval 옵션 값을 가져올 메모리
245 * @param optlen 옵션 길이를 가져올 메모리
246 */
247int
248CF_Socket_GetOption (const int sock,
249 const int optname,
250 void * optval,
251 size_t * optlen)
252{
253 int result = 0;
254
255 ASSERT_SOCKET (sock);
256
257 result = getsockopt (sock,
258 SOL_SOCKET,
259 optname,
260#if defined(_WIN32) || defined(_WIN64)
261 (char *) optval,
262#else
263 optval,
264#endif
265 (socklen_t *) optlen);
266 if (result < 0)
267 return CF_ERROR_SOCKET_GET_OPTION;
268
269 return CF_OK;
270}
271
272/**
273 * 소켓 연결
274 *
275 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
276 *
277 * @param ip 연결할 호스트의 주소 (도메인 이름 가능)
278 * @param port 연결할 호스트의 포트번호
279 */
280int
281CF_Socket_Connect (const char * ip,
282 const unsigned short port)
283{
284 return CF_Socket_ConnectTimeout (ip, port, CF_SOCKET_NO_TIMEOUT);
285}
286
287/**
288 * 타임아웃 동안 소켓 연결
289 *
290 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
291 *
292 * @param ip 연결할 호스트의 주소 (도메인 이름 가능)
293 * @param port 연결할 호스트의 포트번호
294 * @param timeout 타임아웃 (초)
295 *
296 * @see CF_SOCKET_NO_TIMEOUT
297 */
298int
299CF_Socket_ConnectTimeout (const char * ip,
300 const unsigned short port,
301 const int timeout)
302{
303 int result = 0;
304 int sock = 0;
305 struct sockaddr_in address;
306 struct hostent * hostEnt;
307
308 int retval = 0;
309 size_t length = 0;
310 struct timeval tv;
311 fd_set readfds;
312 fd_set writefds;
313
314 ASSERT_INIT ();
315
316 /* 1. create socket */
317 sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
318 if (sock < 0)
319 return CF_ERROR_SOCKET_CREATE;
320
321 /* 2. set data */
322 address.sin_family = AF_INET;
323 address.sin_port = htons (port);
324 address.sin_addr.s_addr = inet_addr (ip);
325
326 TRY
327 {
328 /* 3. get ip from hostname if inet_addr() is failed */
329 if (address.sin_addr.s_addr == (unsigned int)-1)
330 {
331 hostEnt = gethostbyname (ip);
332 if (hostEnt == NULL)
333 {
334 result = CF_ERROR_SOCKET_GET_HOST;
335 TRY_BREAK;
336 }
337
338 address.sin_family = (sa_family_t) hostEnt->h_addrtype;
339 memcpy (&(address.sin_addr.s_addr),
340 hostEnt->h_addr,
341 (size_t) hostEnt->h_length);
342 }
343
344 /* 4. set options */
345 if (CF_Socket_Local_SetLinger (sock) < 0 ||
346 CF_Socket_Local_SetReUseAddr (sock) < 0 )
347 {
348 result = CF_ERROR_SOCKET_SET_OPTION;
349 TRY_BREAK;
350 }
351
352 if (timeout > CF_SOCKET_NO_TIMEOUT)
353 CF_Socket_Local_SetNonBlocking (sock, CF_TRUE);
354
355 /* 5. connect */
356 result = connect (sock, (struct sockaddr *) &address, sizeof (address));
357 if (result == 0)
358 {
359 result = CF_OK;
360 TRY_BREAK;
361 }
362
363 /* 6. if connect() is returned, check timeout */
364 if (timeout == CF_SOCKET_NO_TIMEOUT)
365 {
366 result = CF_ERROR_SOCKET_TIMEOUT;
367 TRY_BREAK;
368 }
369
370 FD_ZERO (&readfds);
371 FD_SET ((unsigned int)sock, &readfds);
372 writefds = readfds;
373
374 tv.tv_sec = timeout;
375 tv.tv_usec = 0;
376
377 result = select (sock + 1, &readfds, &writefds, NULL, &tv);
378 if (result == 0)
379 {
380 result = CF_ERROR_SOCKET_TIMEOUT;
381 TRY_BREAK;
382 }
383
384 if (FD_ISSET (sock, &readfds) || FD_ISSET (sock, &writefds))
385 {
386 length = sizeof (retval);
387 retval = 0;
388
389 result = CF_Socket_GetOption (sock, SO_ERROR, &retval, &length);
390 if (result < 0)
391 {
392 result = CF_ERROR_SOCKET_GET_OPTION;
393 TRY_BREAK;
394 }
395 else if (retval)
396 {
397 result = CF_ERROR_SOCKET_CONNECT;
398 TRY_BREAK;
399 }
400 }
401 else
402 {
403 result = CF_ERROR_SOCKET_CONNECT;
404 TRY_BREAK;
405 }
406 }
407 CATCH_IF (result < 0)
408 {
409 CF_Socket_Close (sock);
410 return result;
411 }
412
413 if (timeout > CF_SOCKET_NO_TIMEOUT)
414 CF_Socket_Local_SetNonBlocking (sock, CF_FALSE);
415
416 return sock;
417}
418
419/**
420 * 서버 열기
421 *
422 * @return 성공 시, 서버 소켓; 실패 시, 오류 코드
423 *
424 * @param port 서버 포트
425 * @param backlog listen 시의 backlog 수
426 */
427int
428CF_Socket_Server (const unsigned short port,
429 const int backlog)
430{
431 int result = 0;
432 int sock = 0;
433 struct sockaddr_in address;
434
435 ASSERT_INIT ();
436
437 sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
438 if (sock < 0)
439 return CF_ERROR_SOCKET_CREATE;
440
441 address.sin_family = AF_INET;
442 address.sin_addr.s_addr = htonl (INADDR_ANY);
443 address.sin_port = htons (port);
444
445 TRY
446 {
447 if (CF_Socket_Local_SetLinger (sock) < 0 ||
448 CF_Socket_Local_SetReUseAddr (sock) < 0 )
449 {
450 result = CF_ERROR_SOCKET_SET_OPTION;
451 TRY_BREAK;
452 }
453
454 result = bind (sock, (struct sockaddr *) &address, sizeof (struct sockaddr));
455 if (result < 0)
456 {
457 result = CF_ERROR_SOCKET_BIND;
458 TRY_BREAK;
459 }
460
461 result = listen (sock, backlog);
462 if (result < 0)
463 {
464 result = CF_ERROR_SOCKET_LISTEN;
465 TRY_BREAK;
466 }
467 }
468 CATCH_IF (result < 0)
469 {
470 CF_Socket_Close (sock);
471 return result;
472 }
473
474 return sock;
475}
476
477/**
478 * 소켓 연결
479 *
480 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
481 *
482 * @param sock 서버 소켓
483 *
484 * @see CF_Socket_HostInfo
485 */
486int
487CF_Socket_Accept (const int sock)
488{
489 int sockClient = 0;
490 struct sockaddr_in address;
491 socklen_t len = sizeof (address);
492
493 ASSERT_SOCKET (sock);
494
495 sockClient = accept (sock, (struct sockaddr *) &address, &len);
496 if (sockClient < 0)
497 return CF_ERROR_SOCKET_ACCEPT;
498
499 return sockClient;
500}
501
502/**
503 * 데이터 송신
504 *
505 * @return 성공 시, CF_OK; 실패 시, 오류 코드
506 *
507 * @param sock 소켓
508 * @param buf 송신할 데이터
509 * @param len 송신할 데이터의 길이
510 */
511int
512CF_Socket_Send (const int sock,
513 const void * buf,
514 const int len)
515{
516 return CF_Socket_SendTimeout (sock, buf, len, CF_SOCKET_NO_TIMEOUT);
517}
518
519/**
520 * 타임아웃 동안 데이터 송신
521 *
522 * @return 성공 시, CF_OK; 실패 시, 오류 코드
523 *
524 * @param sock 소켓
525 * @param buf 송신할 데이터
526 * @param len 송신할 데이터의 길이
527 * @param timeout 타임아웃 (초)
528 *
529 * @see CF_SOCKET_NO_TIMEOUT
530 */
531int
532CF_Socket_SendTimeout (const int sock,
533 const void * buf,
534 const int len,
535 const int timeout)
536{
537 int result = 0;
538
539 ASSERT_SOCKET (sock);
540
541 result = (int) send (sock, buf, (size_t) len, 0);
542 if (result != len)
543 return CF_ERROR_SOCKET_SEND;
544
545 return CF_OK;
546}
547
548/**
549 * 데이터 수신
550 *
551 * @return 성공 시, 수신한 데이터의 길이; 실패 시, 오류 코드
552 *
553 * @param sock 소켓
554 * @param buf 데이터를 수신할 버퍼
555 * @param len 데이터를 수신할 버퍼의 최대 크기
556 */
557int
558CF_Socket_Recv (const int sock,
559 void * buf,
560 const int len)
561{
562 return CF_Socket_RecvTimeout (sock, buf, len, CF_SOCKET_NO_TIMEOUT);
563}
564
565/**
566 * 타임아웃 동안 데이터 수신
567 *
568 * @return 성공 시, 수신한 데이터의 길이; 실패 시, 오류 코드
569 *
570 * @param sock 소켓
571 * @param buf 데이터를 수신할 버퍼
572 * @param len 데이터를 수신할 버퍼의 최대 크기
573 * @param timeout 타임아웃 (초)
574 *
575 * @see CF_SOCKET_NO_TIMEOUT
576 */
577int
578CF_Socket_RecvTimeout (const int sock,
579 void * buf,
580 const int len,
581 const int timeout)
582{
583 int result = 0;
584
585 ASSERT_SOCKET (sock);
586
587 TRY
588 {
589 result = CF_Socket_Local_CheckTimeout (sock, timeout);
590 if (result < 0)
591 {
592 TRY_BREAK;
593 }
594
595 result = (int) recv (sock, buf, (size_t) len, 0);
596 if (result < 0)
597 {
598 result = CF_ERROR_SOCKET_RECV;
599 TRY_BREAK;
600 }
601 }
602 CATCH_IF (result < 0)
603 {
604 return result;
605 }
606
607 return result;
608}
Note: See TracBrowser for help on using the repository browser.