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

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

#1 fix makefile bug

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