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

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

#1 more fix and arrange doxygen comments

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