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

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

#1 modify docs and some handle closing routine

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