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
Line 
1/**
2 * \file cf_socket.c
3 *
4 * \author myusgun <myusgun@gmail.com>
5 *
6 * \brief TCP 소켓 구현
7 */
8#include "cf_socket.h"
9#include "cf_local.h"
10#include "cf_error.h"
11#include "cf_util.h"
12
13#include <stdio.h>
14#include <string.h>
15
16#if defined(_WIN32) || defined(_WIN64)
17/* for windows {{{ */
18# include <WinSock2.h>
19# pragma comment (lib, "ws2_32.lib")
20/* }}} for windows */
21#else
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>
31# include <sys/types.h>
32# include <sys/time.h>
33/* }}} for linux/unix */
34#endif
35
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
44#define ASSERT_INIT() \
45 if (!CF_Socket_IsInitialized ()) \
46 return CF_ERROR_SOCKET_NOT_INITIALIZED
47
48#define TRY_INIT() \
49 if (CF_Socket_Initialize ()) \
50 return CF_ERROR_SOCKET_NOT_INITIALIZED
51
52#define ASSERT_SOCKET(__sock) \
53 if (__sock < 0) \
54 return CF_ERROR_SOCKET_INVALID_SOCKET
55
56#if defined(_WIN32) || defined(_WIN64)
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
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
75static CF_BOOL gInitialized = CF_FALSE;
76
77static void
78CF_Socket_Local_SetNonBlocking (const int sock,
79 CF_BOOL boolean)
80{
81#if defined(_WIN32) || defined(_WIN64)
82 unsigned long mode = (boolean) ? 1 : 0;
83 ioctlsocket (sock, FIONBIO, &mode);
84#else
85 int flags = fcntl (sock, F_GETFL, 0);
86
87 if (boolean) flags |= O_NONBLOCK;
88 else flags &= ~O_NONBLOCK;
89
90 fcntl (sock, F_SETFL, flags);
91#endif
92}
93
94static int
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
145CF_Socket_Local_SetLinger (const int sock)
146{
147 struct linger linger;
148
149 linger.l_onoff = 1;
150 linger.l_linger = 0;
151
152 return CF_Socket_Local_SetOption (sock,
153 SO_LINGER,
154 &linger,
155 sizeof (linger));
156}
157
158static int
159CF_Socket_Local_SetReUseAddr (const int sock)
160{
161 int reuseaddr = 1;
162
163 return CF_Socket_Local_SetOption (sock,
164 SO_REUSEADDR,
165 &reuseaddr,
166 sizeof (reuseaddr));
167}
168
169static int
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
178 if (timeout <= CF_SOCKET_NO_TIMEOUT)
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
187 while ((result = select (sock + 1, &readfds, NULL, NULL, &tv)) < 0)
188 {
189 error = CF_Util_GetSystemError ();
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)
198 return CF_ERROR_SOCKET_CHECK_DESC_SET;
199
200 return CF_OK;
201}
202
203/**
204 * 소켓의 초기화 상태 확인
205 *
206 * \return 초기화 된 경우, CF_TRUE; 그렇지 않은 경우, CF_FALSE
207 */
208static CF_BOOL
209CF_Socket_IsInitialized (void)
210{
211 return gInitialized;
212}
213
214/**
215 * 소켓 초기화
216 *
217 * \return 성공 시, CF_OK; 실패 시, 오류 코드
218 */
219int
220CF_Socket_Initialize (void)
221{
222 if (!CF_Socket_IsInitialized ())
223 {
224#if defined(_WIN32) || defined(_WIN64)
225 WSADATA winsockData;
226 if (WSAStartup (MAKEWORD (2, 0), &winsockData))
227 return CF_ERROR_SOCKET_INITIALIZE;
228#endif
229 gInitialized = CF_TRUE;
230 }
231
232 return CF_OK;
233}
234
235/**
236 * 소켓 해제
237 *
238 * \return 성공 시, CF_OK; 실패 시, 오류 코드
239 */
240int
241CF_Socket_Finalize (void)
242{
243 ASSERT_INIT ();
244
245#if defined(_WIN32) || defined(_WIN64)
246 if (WSACleanup ())
247 return CF_ERROR_SOCKET_FINALIZE;
248#endif
249
250 return CF_OK;
251}
252
253/**
254 * 소켓 컨텍스트 생성
255 *
256 * \return 성공 시, CF_OK; 실패 시, 오류 코드
257 *
258 * \param ctx 소켓 컨텍스트
259 */
260int
261CF_Socket_Create (cf_ctx * ctx)
262{
263 int result = 0;
264
265 CF_SOCKET_CONTEXT * context = NULL;
266
267 ASSERT_CTX (ctx);
268
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 }
277
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;
294}
295
296/**
297 * 소켓 닫기
298 *
299 * \return 성공 시, CF_OK; 실패 시, 오류 코드
300 *
301 * \param ctx 소켓 컨텍스트
302 */
303int
304CF_Socket_Close (cf_ctx ctx)
305{
306 int result = 0;
307 int sock = 0;
308
309 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
310
311 ASSERT_CTX (context);
312 ASSERT_SOCKET (context->sock);
313
314 sock = context->sock;
315
316 free (context);
317
318 result = close (sock);
319
320 if (result != 0)
321 return CF_ERROR_SOCKET_CLOSE;
322
323 return CF_OK;
324}
325
326/**
327 * 소켓 연결
328 *
329 * \return 성공 시, CF_OK; 실패 시, 오류 코드
330 *
331 * \param ctx 소켓 컨텍스트
332 * \param ip 연결할 호스트의 주소 (도메인 이름 가능)
333 * \param port 연결할 호스트의 포트번호
334 */
335int
336CF_Socket_Connect (cf_ctx ctx,
337 const char * ip,
338 const unsigned short port)
339{
340 int result = 0;
341 int sock = 0;
342 int timeout = 0;
343 struct sockaddr_in address;
344 struct hostent * hostEnt;
345
346 int retval = 0;
347 size_t length = 0;
348 struct timeval tv;
349 fd_set readfds;
350 fd_set writefds;
351
352 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
353
354 ASSERT_CTX (ctx);
355 TRY_INIT ();
356
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
363 /* 2. set data */
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 {
370 /* 3. get ip from hostname if inet_addr() is failed */
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;
381 memcpy (&(address.sin_addr.s_addr),
382 hostEnt->h_addr,
383 (size_t) hostEnt->h_length);
384 }
385
386 /* 4. set options */
387 if (CF_Socket_Local_SetLinger (sock) < 0 ||
388 CF_Socket_Local_SetReUseAddr (sock) < 0 )
389 {
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
431 result = CF_Socket_GetOption (ctx, SO_ERROR, &retval, &length);
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 {
445 result = CF_ERROR_SOCKET_CONNECT;
446 TRY_BREAK;
447 }
448 }
449 CATCH_IF (result < 0)
450 {
451 CF_Socket_Close (ctx);
452 return result;
453 }
454
455 if (timeout > CF_SOCKET_NO_TIMEOUT)
456 CF_Socket_Local_SetNonBlocking (sock, CF_FALSE);
457
458 return result;
459}
460
461/**
462 * 서버 열기
463 *
464 * \return 성공 시, CF_OK; 실패 시, 오류 코드
465 *
466 * \param ctx 소켓 컨텍스트
467 * \param port 서버 포트
468 * \param backlog listen 시의 backlog 수
469 */
470int
471CF_Socket_Server (cf_ctx ctx,
472 const unsigned short port,
473 const int backlog)
474{
475 int result = 0;
476 int sock = 0;
477 struct sockaddr_in address;
478
479 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
480
481 ASSERT_CTX (ctx);
482 TRY_INIT ();
483
484 sock = context->sock;
485 context->port = port;
486
487 address.sin_family = AF_INET;
488 address.sin_addr.s_addr = htonl (INADDR_ANY);
489 address.sin_port = htons (port);
490
491 TRY
492 {
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
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
507 snprintf (context->ip, sizeof (context->ip),
508 "%s",
509 inet_ntoa(address.sin_addr));
510
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 {
520 CF_Socket_Close (ctx);
521 }
522
523 return result;
524}
525
526/**
527 * 소켓 연결 받기
528 *
529 * \return 성공 시, CF_OK; 실패 시, 오류 코드
530 *
531 * \param ctx 소켓 컨텍스트
532 * \param client 연결을 수락할 client 소켓 컨텍스트
533 */
534int
535CF_Socket_Accept (const cf_ctx ctx,
536 cf_ctx * client)
537{
538 int result = 0;
539 struct sockaddr_in address;
540 socklen_t len = sizeof (address);
541
542 CF_SOCKET_CONTEXT * clntctx = NULL;
543 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
544
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);
559 return CF_ERROR_SOCKET_ACCEPT;
560 }
561
562 *client = (cf_ctx) clntctx;
563
564 return CF_OK;
565}
566
567/**
568 * 데이터 송신
569 *
570 * \return 성공 시, CF_OK; 실패 시, 오류 코드
571 *
572 * \param ctx 소켓 컨텍스트 * \param buf 송신할 데이터
573 * \param len 송신할 데이터의 길이
574 */
575int
576CF_Socket_Send (const cf_ctx ctx,
577 const void * buf,
578 const size_t len)
579{
580 int result = 0;
581
582 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
583
584 ASSERT_CTX (context);
585 ASSERT_SOCKET (context->sock);
586
587 result = (int) send (context->sock, buf, len, 0);
588 if ((size_t) result != len)
589 return CF_ERROR_SOCKET_SEND;
590
591 return CF_OK;
592}
593
594/**
595 * 데이터 수신
596 *
597 * \return 성공 시, 수신한 데이터의 길이; 실패 시, 오류 코드
598 *
599 * \param ctx 소켓 컨텍스트
600 * \param buf 데이터를 수신할 버퍼
601 * \param len 데이터를 수신할 버퍼의 최대 크기
602 */
603int
604CF_Socket_Recv (const cf_ctx ctx,
605 void * buf,
606 const size_t len)
607{
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;
624}
625
626/**
627 * 소켓 옵션 설정
628 *
629 * \return 성공 시 CF_OK; 실패 시, 오류 코드
630 *
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 * 소켓 옵션 얻기
654 *
655 * \return 성공 시 CF_OK; 실패 시, 오류 코드
656 *
657 * \param ctx 소켓 컨텍스트
658 * \param optname 옵션 이름
659 * \param optval 옵션 값을 가져올 메모리
660 * \param optlen 옵션 길이를 가져올 메모리
661 */
662int
663CF_Socket_GetOption (const cf_ctx ctx,
664 const int optname,
665 void * optval,
666 size_t * optlen)
667{
668 CF_SOCKET_CONTEXT * context = (CF_SOCKET_CONTEXT *) ctx;
669
670 ASSERT_CTX (context);
671
672 return CF_Socket_Local_GetOption (context->sock,
673 optname,
674 optval,
675 optlen);
676}
677
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;
691
692 ASSERT_CTX (context);
693
694 context->timeout = timeout;
695
696 return CF_OK;
697}
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.