SOCKET sock_init_connection(const char *address, int port, int maxcli)
{
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN sin = { 0 };

    if(sock == INVALID_SOCKET)
    {
        int err = sock_error();
        fprintf (stderr, "socket() : %s\n", sock_err_message(err));
        return err;
    }

    if(!sock_getAddr(sin, address))
    {
        fprintf (stderr, "connection failed\n");
        return EXIT_FAILURE;
    }

    sin.sin_port = htons(port);
    sin.sin_family = AF_INET;

    if(address != NULL && strlen(address) > 0)
    {
        if(connect(sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR)
        {
            int err = sock_error();
            fprintf (stderr, "connect() : %s\n", sock_err_message(err));
            return err;
        }
    }
    else
    {
        if(bind(sock,(SOCKADDR *) &sin, sizeof sin) == SOCKET_ERROR)
        {
            int err = sock_error();
            fprintf (stderr, "socket() : %s\n", sock_err_message(err));
            return err;
        }

        if(listen(sock, maxcli) == SOCKET_ERROR)
        {
            int err = sock_error();
            fprintf (stderr, "listen() : %s\n", sock_err_message(err));
            return err;
        }
    }

   return sock;
}
uint32_t ClientNetSocket::receive_buf(uint8_t* buf, uint32_t max_size, bool& shutdown)
{
    uint8_t* pos = buf;
    ASSERT(_peer != INVALID_SOCKET);

    shutdown = false;

    while (max_size) {
        int now;
        if ((now = ::recv(_peer, (char*)pos, max_size, 0)) <= 0) {
            if (now == 0) {
                shutdown = true;
                break; // a case where fin is received, but before that, there is a msg
            }

            int err = sock_error();

            if (err == WOULDBLOCK_ERR) {
                break;
            }

            if (err == INTERRUPTED_ERR) {
                continue;
            }

            LOG_INFO("receive in connection_id=%d failed errno=%s", _id, sock_err_message(err));
            throw ClientNetSocket::ReceiveException();
        }
        max_size -= now;
        pos += now;
    }

    return (pos - buf);
}
int sock_end_connection(int sock)
{
    if(closesocket(sock) == -1)
    {
        int err = sock_error();
        fprintf(stderr, "closesocket() : %s\n", sock_err_message(err));
        return err;
    }

    return  0;
}
bool ClientNetSocket::connect(uint32_t recv_tokens)
{
    struct sockaddr_in addr;
    int no_delay;


    ASSERT(_peer == INVALID_SOCKET && _status == SOCKET_STATUS_CLOSED);

    addr.sin_port = _local_port;
    addr.sin_addr.s_addr = _local_addr.s_addr;
    addr.sin_family = AF_INET;

    if ((_peer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
        int err = sock_error();
        THROW("%s: failed to create socket: %s", __FUNCTION__, sock_err_message(err));
    }

    no_delay = 1;
    if (setsockopt(_peer, IPPROTO_TCP, TCP_NODELAY,
                   (const char*)&no_delay, sizeof(no_delay)) == SOCKET_ERROR) {
        LOG_WARN("set TCP_NODELAY failed");
    }

    LOG_INFO("connect to ip=%s port=%d (connection_id=%d)",
             inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), _id);

    if (::connect(_peer, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR) {
        int err = sock_error();
        closesocket(_peer);
        _peer = INVALID_SOCKET;
        LOG_INFO("connect to ip=%s port=%d failed %s (connection_id=%d)",
                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), sock_err_message(err), _id);
        return false;
    }

    _process_loop.add_socket(*this);
    _status = SOCKET_STATUS_OPEN;
    _num_recv_tokens = recv_tokens;
    return true;
}
int sock_init()
{
    #ifdef WIN32
        WSADATA wsa;
        int err = WSAStartup(MAKEWORD(2, 2), &wsa);
        if(err < 0)
        {
            fputs(stderr, "WSAStartup failed : %s", sock_err_message(sock_error()));
            return EXIT_FAILURE;
        }
    #endif

    return 0;
}
void ClientNetSocket::apply_guest_fin()
{
    if (_status == SOCKET_STATUS_OPEN) {
        if (shutdown(_peer, SHUT_WR) == SOCKET_ERROR) {
            int err = sock_error();
            LOG_INFO("shutdown in connection_id=%d failed %s", _id, sock_err_message(err));
            throw ClientNetSocket::ShutdownExcpetion();
        }

        _fin_pending = false;
        _status = SOCKET_STATUS_SENT_FIN;
    } else if (_status == SOCKET_STATUS_RECEIVED_FIN) {
        close_and_tell();
    }
}
uint32_t ClientNetSocket::send_buf(const uint8_t* buf, uint32_t size)
{
    const uint8_t* pos = buf;
    ASSERT(_peer != INVALID_SOCKET);
    while (size) {
        int now;
        if ((now = ::send(_peer, (char*)pos, size, MSG_NOSIGNAL)) == SOCKET_ERROR) {
            int err = sock_error();
            if (err == WOULDBLOCK_ERR) {
                break;
            }

            if (err == INTERRUPTED_ERR) {
                continue;
            }

            LOG_INFO("send in connection_id=%d failed %s", _id, sock_err_message(err));
            throw ClientNetSocket::SendException();
        }
        size -= now;
        pos += now;
    }
    return pos - buf;
}