tipc.c 8.31 KB
/*
 * tipc.c
 *
 *  Created on: 2015. 9. 1.
 *      Author: jwjw
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <linux/tipc.h>

#include <util.h>
#include <tlist.h>
#include <pollmng.h>
#include <tipc.h>

/// tcp 소켓의 형태를 구분한다.
#define  SOCK_TYPE_CLIENT          0
#define  SOCK_TYPE_SERVER          1
#define  SOCK_TYPE_SERVER_CLIENT   2

/// tcp 개별 구조체
typedef struct {

    int   type;
    int   inst;

    // 서버용 정보
    tlist *client_list;
    int    client_max;
    int    sock_type;
    poll_obj_t *obj_server;   // 서버에서 분리된 클라이언트를 위해
} tipc_priv_t;


poll_obj_t  *tipc_open_server( int type, int inst, int client_max)
{
    struct sockaddr_tipc server_addr;

    server_addr.family              = AF_TIPC;
    server_addr.addrtype            = TIPC_ADDR_NAMESEQ;
    server_addr.addr.nameseq.type   = type;
    server_addr.addr.nameseq.lower  = inst;
    server_addr.addr.nameseq.upper  = inst;
    server_addr.scope               = TIPC_ZONE_SCOPE;

    int sockfd  = socket (AF_TIPC, SOCK_SEQPACKET, 0);
    if ( sockfd < 0 ){
        perror( "tipc server open error:" );
        return NULL;
    }


    if (0 != bind( sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
        perror("tipc bind error :");
        close( sockfd );
        return NULL;
    }

    if (0 != listen( sockfd, 0)) {
        perror("tipc listen error :");
        close( sockfd );
        return NULL;
    }

    // tcp 만의 정보를 설정한다.
    tipc_priv_t *tipc   = (tipc_priv_t *)malloc( sizeof(tipc_priv_t) );

    tipc->sock_type     = SOCK_TYPE_SERVER;
    tipc->type          = type;
    tipc->inst          = inst;
    tipc->client_max    = client_max;
    tipc->client_list   = tlist_create();

    // 폴관리객체에 등록한다.
    poll_obj_t *obj = poll_add( sockfd );
    obj->type = STYP_TIPC;
    obj->priv = (void *)tipc;

    poll_rebuild();

    return obj;
}

poll_obj_t  *tipc_open_client( int type, int inst )
{
    struct sockaddr_tipc server_addr;


    server_addr.family                  = AF_TIPC;
    server_addr.addrtype                = TIPC_ADDR_NAME;
    server_addr.addr.name.name.type     = type;
    server_addr.addr.name.name.instance = inst;
    server_addr.addr.name.domain        = 0;

    int sockfd  = socket (AF_TIPC, SOCK_SEQPACKET, 0);
    if ( sockfd < 0 ){
        perror( "tipc open error:" );
        return NULL;
    }

    if ( 0 != connect( sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) ) {
        perror( "tipc connect error :" );
        close( sockfd );
        return NULL;
    }


    poll_obj_t *obj;
    tipc_priv_t *tipc = (tipc_priv_t *)malloc( sizeof(tipc_priv_t) );

    tipc->sock_type     = SOCK_TYPE_SERVER;
    tipc->type          = type;
    tipc->inst          = inst;

    // 폴관리객체에 등록한다.
    obj = poll_add( sockfd );
    obj->type = STYP_TIPC;
    obj->priv = (void *)tipc;

    return obj;
}

//------------------------------------------------------------------------------
/** @brief    tcp 소켓을 close 한다.
    @param    obj  폴객체 포인터
*///----------------------------------------------------------------------------
void tipc_close( poll_obj_t *obj )
{
    tipc_priv_t *tipc, *server_tipc;

    if ( obj->on_disconnect ){                          // 소켓 끊어짐을 사용자에게 알린다.
        obj->on_disconnect( obj );
    }

    tipc = (tipc_priv_t *)obj->priv;

    switch( tipc->sock_type )
    {
    case SOCK_TYPE_SERVER_CLIENT :
        // 서버가 관리하는 클라이언트를 리스트에서 삭제
        server_tipc = (tipc_priv_t *)(tipc->obj_server->priv);
        if ( server_tipc  ){
            tlist_remove( server_tipc->client_list, obj );
        }
        break;
    case SOCK_TYPE_SERVER :
        tlist_free( tipc->client_list );                        // 서버가 관리하는 클라이언트의 server_tipc 를 모두 NULL 로 만든다.
        break;
    }

    shutdown( obj->fd, SHUT_RDWR);
    close( obj->fd );
    if ( obj->priv ){
        free( obj->priv );
    }

    poll_delete( obj );
}


static poll_obj_t  *tipc_connect_client( poll_obj_t *obj_server, int sockfd)
{
    // 클라이언트 접속의 개수를 파악한다.
    tipc_priv_t *server_tipc = (tipc_priv_t *)obj_server->priv;
    if ( tlist_getcount(server_tipc->client_list) >= server_tipc->client_max ){
        shutdown(sockfd, SHUT_RDWR);
        close( sockfd );
        printf( "tipc connect error : client is full (%d)\n", server_tipc->client_max );
        return NULL;
    }

    // tipc 만의 정보를 설정한다.
    tipc_priv_t *tipc = (tipc_priv_t *)malloc( sizeof(tipc_priv_t) );

    tipc->sock_type    = SOCK_TYPE_SERVER_CLIENT;
    tipc->obj_server   = obj_server; // 클라이언트 소켓을 할당한 서버정보

    // 폴관리객체에 등록한다.
    poll_obj_t *obj = poll_add( sockfd );
    if ( obj ){
        // 서버에 접속된 클라이언트를 리스트에 등록
        tlist_add( server_tipc->client_list, obj );

        obj->type = STYP_TIPC;
        obj->priv = (void *)tipc;
        return obj;
    } else {
        shutdown(sockfd, SHUT_RDWR);
        close( sockfd );
        free( tipc );
        return NULL;
    }
}

poll_obj_t  *tipc_accept_client( poll_obj_t *obj_server )
{
    int peer_sd = accept( obj_server->fd, 0, 0);

    if (peer_sd < 0 ) {
        printf ("Server: accept failed\n");
        exit(1);
    }

    return tipc_connect_client( obj_server, peer_sd);
}

//------------------------------------------------------------------------------
/** @brief    tipc 소켓을 통해 데이타를 전송한다.
    @param    obj     폴객체 포인터
    @param    buf     전송버퍼
    @param    len     버퍼의 길이
    @return   전송한 데이타 개수
    @remark   에러에 대한 처리를 해야한다.
              tipc_close( obj ) 를 호출하여 접속을 끊는 방법이 일반적이다.
              BF_POLICY_AUTO_CLOSE 옵션이 있을경우 자동으로 소켓을 닫는다.
*///----------------------------------------------------------------------------
int  tipc_write( poll_obj_t *obj, char *buf, int len )
{
    int  wrcnt = write( obj->fd, buf, len );

    if ( 0 > wrcnt ){
        perror( "[sys]tipc send error:" );
        tipc_close( obj );
    }

    return wrcnt;
}

//------------------------------------------------------------------------------
/** @brief    tipc 소켓을 통해 데이타를 읽는다.
    @param    obj     폴객체 포인터
    @param    len     버퍼의 길이
    @return   전송된 데이타 개수
    @remark   에러에 대한 처리를 해야한다.
              tipc_close( obj ) 를 호출하여 접속을 끊는 방법이 일반적이다.
              BF_POLICY_AUTO_CLOSE 옵션이 있을경우 자동으로 소켓을 닫는다.
*///----------------------------------------------------------------------------
int  tipc_read( poll_obj_t *obj, char *buf, int len )
{
    int rdcnt = read( obj->fd, buf, len );
    if ( 0 > rdcnt ) {
        perror( "tipc recv error:" );

#if 0
        if ( bf_policy & BF_POLICY_CLOSE_IOERR ){
            tipc_close( obj );
        }
#endif
        tipc_close( obj );
    }
    else if ( 0 == rdcnt ){
        if ( 0 > errno )
        {
            perror( "tipc recv error:" );
#if 0
            if ( bf_policy & BF_POLICY_CLOSE_IOERR )
            {
                tipc_close( obj );
            }
#endif
            tipc_close( obj );
        }
        else
        {
#if 0
            if ( bf_policy & BF_POLICY_READ_ZERO_ERROR )
            {
                printf( "tipc read 0 error: close socket\n" );
                tipc_close( obj );
            }
#endif
            tipc_close( obj );
        }
    }

    // 정책에 대한 결정을 한다.
    tipc_priv_t *tipc = (tipc_priv_t *)obj->priv;
    if ( tipc->sock_type  == SOCK_TYPE_SERVER_CLIENT )
    {
#if 0
        if ( bf_policy & BF_POLICY_RELOAD_TIMEOUT )
        {
            tipc->life_sec = get_cur_sec() + tipc_live_sec;
        }
#endif
    }

    return rdcnt;
}