C/C++ IPV6服务器socket绑定在::,接受ipv4链接(双栈)

发布于:2024-05-12 ⋅ 阅读:(65) ⋅ 点赞:(0)

先决条件:

1、 创建IPV6套接字

2、打开套接字可重用

3、禁用仅限 IPV6

                BOOL bEnable = FALSE;
                if (setsockopt(listenfd_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&bEnable), sizeof(bEnable)) < 0)
                {
                    return false;
                }
 

4、绑定套接字

5、使用 struct sockaddr_in6 结构接受套接字,而不是用缺省使用 “sockaddr”,否则 10014(WSAEFAULT)错误,并导致套接字句柄泄漏。

                                struct sockaddr_in6 address = { 0 };
                                int address_size = sizeof(address);

                                int sockfd = WSAAccept(listenfd_, (sockaddr*)&address, &address_size, NULL, NULL);
                                if (sockfd != INVALID_SOCKET)
                                {
                                    AcceptSocketEventArgs e = { Adjust(sockfd) };
                                    OnAcceptSocket(e);
                                }

完整大体代码为;

        bool Open(const char* localIP, int localPort, int backlog) noexcept
        {
            if (localPort < IPEndPoint::MinPort || localPort > IPEndPoint::MaxPort)
            {
                return false;
            }

            if (NULL == localIP || *localIP == '\x0')
            {
                return false;
            }

            if (listenfd_ != INVALID_SOCKET)
            {
                return false;
            }

            if (NULL != hEvent_)
            {
                return false;
            }

            if (NULL != afo_)
            {
                return false;
            }

            if (NULL == context_)
            {
                return false;
            }

            boost::system::error_code ec;
            boost::asio::ip::address bindIP = StringToAddress(localIP, ec);
            if (ec)
            {
                return false;
            }

            if (backlog < 1)
            {
                backlog = PPP_LISTEN_BACKLOG;
            }

            in_ = bindIP.is_v4();
            if (bindIP.is_v6())
            {
                struct sockaddr_in6 in6;
                memset(&in6, 0, sizeof(in6));

                in6.sin6_family = AF_INET6;
                in6.sin6_port = htons(localPort);
                if (inet_pton(AF_INET6, localIP, &in6.sin6_addr) < 1)
                {
                    return false;
                }

                listenfd_ = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
                if (listenfd_ == INVALID_SOCKET)
                {
                    return false;
                }

                Adjust(listenfd_);
                if (!Socket::ReuseSocketAddress(listenfd_, true))
                {
                    return false;
                }

                BOOL bEnable = FALSE;
                if (setsockopt(listenfd_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&bEnable), sizeof(bEnable)) < 0)
                {
                    return false;
                }

                if (bind(listenfd_, reinterpret_cast<sockaddr*>(&in6), sizeof(in6)) < 0)
                {
                    return false;
                }
            }
            elif(bindIP.is_v4())
            {
                struct sockaddr_in in4;
                memset(&in4, 0, sizeof(in4));

                in4.sin_family = AF_INET;
                in4.sin_port = htons(localPort);
                if (inet_pton(AF_INET, localIP, &in4.sin_addr) < 1)
                {
                    return false;
                }

                listenfd_ = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
                if (listenfd_ == INVALID_SOCKET)
                {
                    return false;
                }

                Adjust(listenfd_);
                if (!Socket::ReuseSocketAddress(listenfd_, true))
                {
                    return false;
                }

                if (bind(listenfd_, reinterpret_cast<sockaddr*>(&in4), sizeof(in4)) < 0)
                {
                    return false;
                }
            }
            else
            {
                return false;
            }

            if (listen(listenfd_, backlog) < 0)
            {
                return false;
            }

            hEvent_ = WSACreateEvent();
            if (hEvent_ == WSA_INVALID_EVENT)
            {
                return false;
            }

            if (WSAEventSelect(listenfd_, hEvent_, FD_ACCEPT | FD_CLOSE) != NOERROR)
            {
                return false;
            }

            afo_ = make_shared_void_pointer<boost::asio::windows::object_handle>(*context_, hEvent_);
            return Next();
        }

接受套接字连入部分:

WSANETWORKEVENTS events;
if (WSAEnumNetworkEvents(listenfd, hEvent, &events) == NOERROR)
{
    if (events.lNetworkEvents & FD_ACCEPT)
    {
        if (events.iErrorCode[FD_ACCEPT_BIT] == 0)
        {
            struct sockaddr_in6 address = { 0 };
            int address_size = sizeof(address);

            int sockfd = WSAAccept(listenfd_, (sockaddr*)&address, &address_size, NULL, NULL);
            if (sockfd != INVALID_SOCKET)
            {
                AcceptSocketEventArgs e = { Adjust(sockfd) };
                OnAcceptSocket(e);
            }
        }
    }
    elif(events.lNetworkEvents & FD_CLOSE)
    {
        if (events.iErrorCode[FD_ACCEPT_BIT] == 0) /* event is operation_canceled. */
        {
            return;
        }
    }
}