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
Line 
1/**
2 * @file cf_socket.c
3 * @author myusgun <myusgun@gmail.com>
4 *
5 * @brief TCP 소켓 구현
6 */
7#include "cf_socket.h"
8#include "cf_local.h"
9#include "cf_error.h"
10
11#include <stdio.h>
12#include <string.h>
13
14#if defined(_WIN32) || defined(_WIN64)
15/* for windows {{{ */
16# include <WinSock2.h>
17# pragma comment (lib, "ws2_32.lib")
18/* }}} for windows */
19#else
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>
29# include <sys/types.h>
30# include <sys/time.h>
31/* }}} for linux/unix */
32#endif
33
34#define ASSERT_INIT() \
35 if (!CF_Socket_IsInitialized ()) \
36 return CF_ERROR_SOCKET_NOT_INITIALIZED
37
38#define ASSERT_SOCKET(__sock) \
39 if (__sock < 0) \
40 return CF_ERROR_SOCKET_INVALID_SOCKET
41
42#if defined(_WIN32) || defined(_WIN64)
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
53static CF_BOOL gInitialized = CF_FALSE;
54
55static void
56CF_Socket_Local_SetNonBlocking (const int sock,
57 CF_BOOL boolean)
58{
59#if defined(_WIN32) || defined(_WIN64)
60 unsigned long mode = (boolean) ? 1 : 0;
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
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
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
117 while ((result = select (sock + 1, &readfds, NULL, NULL, &tv)) < 0)
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
135/**
136 * 소켓의 초기화 상태 확인
137 *
138 * @return 초기화 된 경우, CF_TRUE; 그렇지 않은 경우, CF_FALSE
139 */
140CF_BOOL
141CF_Socket_IsInitialized (void)
142{
143 return gInitialized;
144}
145
146/**
147 * 소켓 초기화
148 *
149 * @return 성공 시, CF_OK; 실패 시, 오류 코드
150 */
151int
152CF_Socket_Initialize (void)
153{
154#if defined(_WIN32) || defined(_WIN64)
155 WSADATA winsockData;
156 if (WSAStartup (MAKEWORD (2, 0), &winsockData))
157 return CF_ERROR_SOCKET_INITIALIZE;
158#endif
159 gInitialized = CF_TRUE;
160
161 return CF_OK;
162}
163
164/**
165 * 소켓 해제
166 *
167 * @return 성공 시, CF_OK; 실패 시, 오류 코드
168 */
169int
170CF_Socket_Finalize (void)
171{
172 ASSERT_INIT ();
173
174#if defined(_WIN32) || defined(_WIN64)
175 if (WSACleanup ())
176 return CF_ERROR_SOCKET_FINALIZE;
177#endif
178
179 return CF_OK;
180}
181
182/**
183 * 소켓 닫기
184 *
185 * @return 성공 시, CF_OK; 실패 시, 오류 코드
186 *
187 * @param sock 소켓
188 */
189int
190CF_Socket_Close (const int sock)
191{
192 int result = 0;
193
194 ASSERT_SOCKET (sock);
195
196 result = close (sock);
197
198 if (result != 0)
199 return CF_ERROR_SOCKET_CLOSE;
200
201 return CF_OK;
202}
203
204/**
205 * 소켓 옵션 설정
206 *
207 * @return 성공 시 CF_OK; 실패 시, 오류 코드
208 *
209 * @param sock 소켓
210 * @param optname 옵션 이름
211 * @param optval 설정할 옵션 값의 메모리
212 * @param optlen 설정할 옵션의 길이
213 */
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
222 ASSERT_SOCKET (sock);
223
224 result = setsockopt (sock,
225 SOL_SOCKET,
226 optname,
227#if defined(_WIN32) || defined(_WIN64)
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
239/**
240 * 소켓 옵션 얻기
241 *
242 * @return 성공 시 CF_OK; 실패 시, 오류 코드
243 *
244 * @param sock 소켓
245 * @param optname 옵션 이름
246 * @param optval 옵션 값을 가져올 메모리
247 * @param optlen 옵션 길이를 가져올 메모리
248 */
249int
250CF_Socket_GetOption (const int sock,
251 const int optname,
252 void * optval,
253 size_t * optlen)
254{
255 int result = 0;
256
257 ASSERT_SOCKET (sock);
258
259 result = getsockopt (sock,
260 SOL_SOCKET,
261 optname,
262#if defined(_WIN32) || defined(_WIN64)
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
274/**
275 * 소켓 연결
276 *
277 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
278 *
279 * @param ip 연결할 호스트의 주소 (도메인 이름 가능)
280 * @param port 연결할 호스트의 포트번호
281 */
282int
283CF_Socket_Connect (const char * ip,
284 const unsigned short port)
285{
286 return CF_Socket_ConnectTimeout (ip, port, CF_SOCKET_NO_TIMEOUT);
287}
288
289/**
290 * 타임아웃 동안 소켓 연결
291 *
292 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
293 *
294 * @param ip 연결할 호스트의 주소 (도메인 이름 가능)
295 * @param port 연결할 호스트의 포트번호
296 * @param timeout 타임아웃 (초)
297 *
298 * @see CF_SOCKET_NO_TIMEOUT
299 */
300int
301CF_Socket_ConnectTimeout (const char * ip,
302 const unsigned short port,
303 const int timeout)
304{
305 int result = 0;
306 int sock = 0;
307 struct sockaddr_in address;
308 struct hostent * hostEnt;
309
310 int retval = 0;
311 size_t length = 0;
312 struct timeval tv;
313 fd_set readfds;
314 fd_set writefds;
315
316 ASSERT_INIT ();
317
318 /* 1. create socket */
319 sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
320 if (sock < 0)
321 return CF_ERROR_SOCKET_CREATE;
322
323 /* 2. set data */
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 {
330 /* 3. get ip from hostname if inet_addr() is failed */
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;
341 memcpy (&(address.sin_addr.s_addr),
342 hostEnt->h_addr,
343 (size_t) hostEnt->h_length);
344 }
345
346 /* 4. set options */
347 if (CF_Socket_Local_SetLinger (sock) < 0 ||
348 CF_Socket_Local_SetReUseAddr (sock) < 0 )
349 {
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 {
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
415 if (timeout > CF_SOCKET_NO_TIMEOUT)
416 CF_Socket_Local_SetNonBlocking (sock, CF_FALSE);
417
418 return sock;
419}
420
421/**
422 * 서버 열기
423 *
424 * @return 성공 시, 서버 소켓; 실패 시, 오류 코드
425 *
426 * @param port 서버 포트
427 * @param backlog listen 시의 backlog 수
428 */
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
437 ASSERT_INIT ();
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
447 TRY
448 {
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
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
479/**
480 * 소켓 연결
481 *
482 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
483 *
484 * @param sock 서버 소켓
485 *
486 * @see CF_Socket_HostInfo
487 */
488int
489CF_Socket_Accept (const int sock)
490{
491 int sockClient = 0;
492 struct sockaddr_in address;
493 socklen_t len = sizeof (address);
494
495 ASSERT_SOCKET (sock);
496
497 sockClient = accept (sock, (struct sockaddr *) &address, &len);
498 if (sockClient < 0)
499 return CF_ERROR_SOCKET_ACCEPT;
500
501 return sockClient;
502}
503
504/**
505 * 데이터 송신
506 *
507 * @return 성공 시, CF_OK; 실패 시, 오류 코드
508 *
509 * @param sock 소켓
510 * @param buf 송신할 데이터
511 * @param len 송신할 데이터의 길이
512 */
513int
514CF_Socket_Send (const int sock,
515 const void * buf,
516 const size_t len)
517{
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,
536 const size_t len,
537 const int timeout)
538{
539 int result = 0;
540
541 ASSERT_SOCKET (sock);
542
543 result = (int) send (sock, buf, len, 0);
544 if (result != len)
545 return CF_ERROR_SOCKET_SEND;
546
547 return CF_OK;
548}
549
550/**
551 * 데이터 수신
552 *
553 * @return 성공 시, 수신한 데이터의 길이; 실패 시, 오류 코드
554 *
555 * @param sock 소켓
556 * @param buf 데이터를 수신할 버퍼
557 * @param len 데이터를 수신할 버퍼의 최대 크기
558 */
559int
560CF_Socket_Recv (const int sock,
561 void * buf,
562 const size_t len)
563{
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,
582 const size_t len,
583 const int timeout)
584{
585 int result = 0;
586
587 ASSERT_SOCKET (sock);
588
589 result = CF_Socket_Local_CheckTimeout (sock, timeout);
590 if (result < 0)
591 return result;
592
593 result = (int) recv (sock, buf, len, 0);
594 if (result < 0)
595 return CF_ERROR_SOCKET_RECV;
596
597 return result;
598}
Note: See TracBrowser for help on using the repository browser.