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

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

#1 fix compilation error

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