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