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

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

#1 fix doxygen documentation

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