Changeset 46 in libcf for trunk/src/cf_socket.c


Ignore:
Timestamp:
03/29/13 14:40:25 (11 years ago)
Author:
cheese
Message:

#1 change timeout mechanism from setting socket option to using select

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/cf_socket.c

    r45 r46  
    88#include "cf_error.h"
    99
     10#include <stdio.h>
     11#include <string.h>
     12
    1013#ifdef _WIN32
     14/* for windows {{{ */
     15# include <WinSock2.h>
    1116# pragma comment (lib, "ws2_32.lib")
    12 # define close(__sock)  closesocket(__sock)
    13 # define sa_family_t    unsigned short
     17/* }}} for windows */
    1418#else
    15 #endif
    16 
    17 #include <string.h>
     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
    1830
    1931#define CHECK_SOCKET_INIT()                 \
     
    2537        return CF_ERROR_SOCKET_INVALID_SOCKET
    2638
     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
    2752static 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}
    28108
    29109/**
     
    46126CF_Socket_Initialize (void)
    47127{
    48     int result = 0;
    49 
    50128#ifdef WIN32
    51129    WSADATA winsockData;
    52     result = WSAStartup (MAKEWORD (2, 0), &winsockData);
     130    if (WSAStartup (MAKEWORD (2, 0), &winsockData))
     131       return CF_ERROR_SOCKET_INITIALIZE;
    53132#endif
    54133    gInitialized = CF_TRUE;
    55 
    56     if (result != 0)
    57        return CF_ERROR_SOCKET_INITIALIZE;
    58134   
    59135    return CF_OK;
     
    68144CF_Socket_Finalize (void)
    69145{
    70     int result = 0;
    71 
    72146    CHECK_SOCKET_INIT ();
    73147
    74148#ifdef WIN32
    75     result = WSACleanup ();
    76 #endif
    77     if (result != 0)
     149    if (WSACleanup ())
    78150       return CF_ERROR_SOCKET_FINALIZE;
     151#endif
    79152
    80153    return CF_OK;
     
    174247
    175248/**
    176  * 소켓에 타임아웃 설정
    177  *
    178  * @return 성공 시 CF_OK; 실패 시, 오류 코드
    179  *
    180  * @param sock      소켓
    181  * @param timeout   타임아웃(sec)
    182  */
    183 int
    184 CF_Socket_SetTimeout (const int sock,
    185                       const int timeout)
    186 {
    187     int result = 0;
    188 
    189 #ifdef _WIN32
    190     int time_ms = timeout * 1000;
    191 #else
    192     struct timeval  timeval;
    193     timeval.tv_sec = timeout;
    194     timeval.tv_usec= 0;
    195 #endif
    196 
    197     CHECK_INVALID_SOCKET (sock);
    198 
    199     if (timeout < 0)
    200         return CF_ERROR_SOCKET_INVALID_ARGS;
    201 
    202     result = CF_Socket_SetOption (sock,
    203                                   SO_RCVTIMEO,
    204 #ifdef _WIN32
    205                                   &time_ms,
    206                                   sizeof (time_ms));
    207 #else
    208                                   &timeval,
    209                                   sizeof (timeval));
    210 #endif
    211     if (result < 0)
    212         return CF_ERROR_SOCKET_SET_TIMEOUT;
    213 
    214     return CF_OK;
    215 }
    216 
    217 /**
    218249 * 소켓 연결
    219250 *
    220251 * @return 성공 시, 연결된 소켓; 실패 시, 오류 코드
    221252 *
    222  * @param ip    연결할 호스트의 주소 (도메인 이름 가능)
    223  * @param port  연결할 호스트의 포트번호
     253 * @param ip        연결할 호스트의 주소 (도메인 이름 가능)
     254 * @param port      연결할 호스트의 포트번호
    224255 */
    225256int
    226257CF_Socket_Connect (const char           * ip,
    227258                   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)
    228278{
    229279    int                 result = 0;
     
    231281    struct sockaddr_in  address;
    232282    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;
    233290
    234291    CHECK_SOCKET_INIT ();
    235292
     293    /* 1. create socket */
    236294    sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
    237295    if (sock < 0)
    238296        return CF_ERROR_SOCKET_CREATE;
    239297
     298    /* 2. set data */
    240299    address.sin_family      = AF_INET;
    241300    address.sin_port        = htons (port);
    242301    address.sin_addr.s_addr = inet_addr (ip);
    243302
     303    linger.l_onoff = 1;
     304    linger.l_linger = 0;
     305
    244306    TRY
    245307    {
     308        /* 3. get ip from hostname if inet_addr() is failed */
    246309        if (address.sin_addr.s_addr == (unsigned int)-1)
    247310        {
     
    254317
    255318            address.sin_family = (sa_family_t) hostEnt->h_addrtype;
    256             memcpy (&(address.sin_addr.s_addr), hostEnt->h_addr, (size_t) hostEnt->h_length);
    257         }
    258 
     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 */
    259336        result = connect (sock, (struct sockaddr *) &address, sizeof (address));
    260         if (result < 0)
     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
    261382        {
    262383            result = CF_ERROR_SOCKET_CONNECT;
     
    269390        return result;
    270391    }
     392
     393    if (timeout > CF_SOCKET_NO_TIMEOUT)
     394        CF_Socket_Local_SetNonBlocking (sock, CF_FALSE);
    271395
    272396    return sock;
     
    330454 *
    331455 * @param sock      서버 소켓
    332  * @param address   [옵션] 클라이언트 정보를 담을 sockaddr_in 구조체 포인터
     456 * @param address   [옵션] 클라이언트 정보를 담을 CF_Socket_HostInfo 구조체 포인터
     457 *
     458 * @see CF_Socket_HostInfo
    333459 */
    334460int
    335461CF_Socket_Accept (const int             sock,
    336                   struct sockaddr_in    * address)
    337 {
    338     int                 sockClient;
    339     struct sockaddr_in  remoteAddress;
    340     socklen_t           len = sizeof (remoteAddress);
     462                  CF_Socket_HostInfo    * host)
     463{
     464    int                 sockClient = 0;
     465    struct sockaddr_in  address;
     466    socklen_t           len = sizeof (address);
    341467
    342468    CHECK_INVALID_SOCKET (sock);
    343469
    344     sockClient = accept (sock, (struct sockaddr *) &remoteAddress, &len);
     470    sockClient = accept (sock, (struct sockaddr *) &address, &len);
    345471    if (sockClient < 0)
    346472        return CF_ERROR_SOCKET_ACCEPT;
    347473
    348     if (address != NULL)
    349         memcpy ((void *) address, (void *) &remoteAddress, sizeof (struct sockaddr_in));
     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    }
    350481
    351482    return sockClient;
     
    366497                const int   len)
    367498{
     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{
    368520    int result = 0;
    369521
     
    391543                const int   len)
    392544{
     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{
    393566    int result = 0;
    394567
    395568    CHECK_INVALID_SOCKET (sock);
    396569
    397     result = (int) recv (sock, buf, (size_t) len, 0);
    398     if (result < 0)
    399         return CF_ERROR_SOCKET_RECV;
     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    }
    400589
    401590    return result;
Note: See TracChangeset for help on using the changeset viewer.