【WinSock】TCP UDP Boardcast Multicast

1. 辅助函数(common.h)

#ifndef CPP_NETWORK_COMMUNICATION_COMMON_H
#define CPP_NETWORK_COMMUNICATION_COMMON_H

#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <cstdio>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")

#define INTERFACE_ADDRESS "127.0.0.1"
#define MULTICAST_ADDRESS "239.127.3.5"
#define MULTICAST_PORT 7667
#define BUFFER_SIZE (1024)
#define TCP_SERVER_PORT (8000)
#define TCP_Client_PORT (8001)
#define UDP_SERVER_PORT (7667)
#define UDP_CLIENT_PORT (7668)
#define COUNT (5)

#ifdef DEBUG
#define DEBUG_FUNCTION_NAME()                                                         \
    {                                                                                 \
        printf("================================================================\n"); \
        printf("%s\n", __func__);                                                     \
    }

#define DEBUG_LOG(format, ...)                                                          \
    {                                                                                   \
        printf("LOG:[%s:%d->%s] " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
    }
#else
#define DEBUG_FUNCTION_NAME()
#define DEBUG_LOG(format, ...)
#endif

void show_error_code()
{
    int error_code = WSAGetLastError();
    switch (error_code)
    {
    case 0:
        std::cout << "ERROR CODE (" << error_code << " ): SUCCESS" << std::endl;
        break;
    case WSA_INVALID_HANDLE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_INVALID_HANDLE" << std::endl;
        break;
    case WSA_NOT_ENOUGH_MEMORY:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_NOT_ENOUGH_MEMORY" << std::endl;
        break;
    case WSA_INVALID_PARAMETER:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_INVALID_PARAMETER" << std::endl;
        break;
    case WSA_OPERATION_ABORTED:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_OPERATION_ABORTED" << std::endl;
        break;
    case WSA_IO_INCOMPLETE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_IO_INCOMPLETE" << std::endl;
        break;
    case WSA_IO_PENDING:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_IO_PENDING" << std::endl;
        break;
    case WSAEINTR:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEINTR" << std::endl;
        break;
    case WSAEBADF:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEBADF" << std::endl;
        break;
    case WSAEACCES:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEACCES" << std::endl;
        break;
    case WSAEFAULT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEFAULT" << std::endl;
        break;
    case WSAEINVAL:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEINVAL" << std::endl;
        break;
    case WSAEMFILE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEMFILE" << std::endl;
        break;
    case WSAEWOULDBLOCK:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEWOULDBLOCK" << std::endl;
        break;
    case WSAEINPROGRESS:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEINPROGRESS" << std::endl;
        break;
    case WSAEALREADY:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEALREADY" << std::endl;
        break;
    case WSAENOTSOCK:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOTSOCK" << std::endl;
        break;
    case WSAEDESTADDRREQ:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEDESTADDRREQ" << std::endl;
        break;
    case WSAEMSGSIZE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEMSGSIZE" << std::endl;
        break;
    case WSAEPROTOTYPE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEPROTOTYPE" << std::endl;
        break;
    case WSAENOPROTOOPT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOPROTOOPT" << std::endl;
        break;
    case WSAEPROTONOSUPPORT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEPROTONOSUPPORT" << std::endl;
        break;
    case WSAESOCKTNOSUPPORT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAESOCKTNOSUPPORT" << std::endl;
        break;
    case WSAEOPNOTSUPP:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEOPNOTSUPP" << std::endl;
        break;
    case WSAEADDRINUSE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEADDRINUSE" << std::endl;
        break;
    case WSAEADDRNOTAVAIL:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEADDRNOTAVAIL" << std::endl;
        break;
    case WSAENETDOWN:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENETDOWN" << std::endl;
        break;
    case WSAENETUNREACH:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENETUNREACH" << std::endl;
        break;
    case WSAENETRESET:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENETRESET" << std::endl;
        break;
    case WSAECONNABORTED:
        std::cout << "ERROR CODE (" << error_code << " ): WSAECONNABORTED" << std::endl;
        break;
    case WSAECONNRESET:
        std::cout << "ERROR CODE (" << error_code << " ): WSAECONNRESET" << std::endl;
        break;
    case WSAENOBUFS:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOBUFS" << std::endl;
        break;
    case WSAEISCONN:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEISCONN" << std::endl;
        break;
    case WSAENOTCONN:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOTCONN" << std::endl;
        break;
    case WSAESHUTDOWN:
        std::cout << "ERROR CODE (" << error_code << " ): WSAESHUTDOWN" << std::endl;
        break;
    case WSAETOOMANYREFS:
        std::cout << "ERROR CODE (" << error_code << " ): WSAETOOMANYREFS" << std::endl;
        break;
    case WSAETIMEDOUT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAETIMEDOUT" << std::endl;
        break;
    case WSAECONNREFUSED:
        std::cout << "ERROR CODE (" << error_code << " ): WSAECONNREFUSED" << std::endl;
        break;
    case WSAELOOP:
        std::cout << "ERROR CODE (" << error_code << " ): WSAELOOP" << std::endl;
        break;
    case WSAENAMETOOLONG:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENAMETOOLONG" << std::endl;
        break;
    case WSAEHOSTDOWN:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEHOSTDOWN" << std::endl;
        break;
    case WSAEHOSTUNREACH:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEHOSTUNREACH" << std::endl;
        break;
    case WSAENOTEMPTY:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOTEMPTY" << std::endl;
        break;
    case WSAEPROCLIM:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEPROCLIM" << std::endl;
        break;
    case WSAEUSERS:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEUSERS" << std::endl;
        break;
    case WSAEDQUOT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEDQUOT" << std::endl;
        break;
    case WSAESTALE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAESTALE" << std::endl;
        break;
    case WSAEREMOTE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEREMOTE" << std::endl;
        break;
    case WSASYSNOTREADY:
        std::cout << "ERROR CODE (" << error_code << " ): WSASYSNOTREADY" << std::endl;
        break;
    case WSAVERNOTSUPPORTED:
        std::cout << "ERROR CODE (" << error_code << " ): WSAVERNOTSUPPORTED" << std::endl;
        break;
    case WSANOTINITIALISED:
        std::cout << "ERROR CODE (" << error_code << " ): WSANOTINITIALISED" << std::endl;
        break;
    case WSAEDISCON:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEDISCON" << std::endl;
        break;
    case WSAENOMORE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAENOMORE" << std::endl;
        break;
    case WSAECANCELLED:
        std::cout << "ERROR CODE (" << error_code << " ): WSAECANCELLED" << std::endl;
        break;
    case WSAEINVALIDPROCTABLE:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEINVALIDPROCTABLE" << std::endl;
        break;
    case WSAEINVALIDPROVIDER:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEINVALIDPROVIDER" << std::endl;
        break;
    case WSAEPROVIDERFAILEDINIT:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEPROVIDERFAILEDINIT" << std::endl;
        break;
    case WSASYSCALLFAILURE:
        std::cout << "ERROR CODE (" << error_code << " ): WSASYSCALLFAILURE" << std::endl;
        break;
    case WSASERVICE_NOT_FOUND:
        std::cout << "ERROR CODE (" << error_code << " ): WSASERVICE_NOT_FOUND" << std::endl;
        break;
    case WSATYPE_NOT_FOUND:
        std::cout << "ERROR CODE (" << error_code << " ): WSATYPE_NOT_FOUND" << std::endl;
        break;
    case WSA_E_NO_MORE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_E_NO_MORE" << std::endl;
        break;
    case WSA_E_CANCELLED:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_E_CANCELLED" << std::endl;
        break;
    case WSAEREFUSED:
        std::cout << "ERROR CODE (" << error_code << " ): WSAEREFUSED" << std::endl;
        break;
    case WSAHOST_NOT_FOUND:
        std::cout << "ERROR CODE (" << error_code << " ): WSAHOST_NOT_FOUND" << std::endl;
        break;
    case WSATRY_AGAIN:
        std::cout << "ERROR CODE (" << error_code << " ): WSATRY_AGAIN" << std::endl;
        break;
    case WSANO_RECOVERY:
        std::cout << "ERROR CODE (" << error_code << " ): WSANO_RECOVERY" << std::endl;
        break;
    case WSANO_DATA:
        std::cout << "ERROR CODE (" << error_code << " ): WSANO_DATA" << std::endl;
        break;
    case WSA_QOS_RECEIVERS:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_RECEIVERS" << std::endl;
        break;
    case WSA_QOS_SENDERS:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_SENDERS" << std::endl;
        break;
    case WSA_QOS_NO_SENDERS:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_NO_SENDERS" << std::endl;
        break;
    case WSA_QOS_NO_RECEIVERS:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_NO_RECEIVERS" << std::endl;
        break;
    case WSA_QOS_REQUEST_CONFIRMED:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_REQUEST_CONFIRMED" << std::endl;
        break;
    case WSA_QOS_ADMISSION_FAILURE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_ADMISSION_FAILURE" << std::endl;
        break;
    case WSA_QOS_POLICY_FAILURE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_POLICY_FAILURE" << std::endl;
        break;
    case WSA_QOS_BAD_STYLE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_BAD_STYLE" << std::endl;
        break;
    case WSA_QOS_BAD_OBJECT:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_BAD_OBJECT" << std::endl;
        break;
    case WSA_QOS_TRAFFIC_CTRL_ERROR:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_TRAFFIC_CTRL_ERROR" << std::endl;
        break;
    case WSA_QOS_GENERIC_ERROR:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_GENERIC_ERROR" << std::endl;
        break;
    case WSA_QOS_ESERVICETYPE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_ESERVICETYPE" << std::endl;
        break;
    case WSA_QOS_EFLOWSPEC:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFLOWSPEC" << std::endl;
        break;
    case WSA_QOS_EPROVSPECBUF:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EPROVSPECBUF" << std::endl;
        break;
    case WSA_QOS_EFILTERSTYLE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFILTERSTYLE" << std::endl;
        break;
    case WSA_QOS_EFILTERTYPE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFILTERTYPE" << std::endl;
        break;
    case WSA_QOS_EFILTERCOUNT:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFILTERCOUNT" << std::endl;
        break;
    case WSA_QOS_EOBJLENGTH:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EOBJLENGTH" << std::endl;
        break;
    case WSA_QOS_EFLOWCOUNT:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFLOWCOUNT" << std::endl;
        break;
    case WSA_QOS_EUNKOWNPSOBJ:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EUNKOWNPSOBJ" << std::endl;
        break;
    case WSA_QOS_EPOLICYOBJ:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EPOLICYOBJ" << std::endl;
        break;
    case WSA_QOS_EFLOWDESC:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EFLOWDESC" << std::endl;
        break;
    case WSA_QOS_EPSFLOWSPEC:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EPSFLOWSPEC" << std::endl;
        break;
    case WSA_QOS_EPSFILTERSPEC:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_EPSFILTERSPEC" << std::endl;
        break;
    case WSA_QOS_ESDMODEOBJ:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_ESDMODEOBJ" << std::endl;
        break;
    case WSA_QOS_ESHAPERATEOBJ:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_ESHAPERATEOBJ" << std::endl;
        break;
    case WSA_QOS_RESERVED_PETYPE:
        std::cout << "ERROR CODE (" << error_code << " ): WSA_QOS_RESERVED_PETYPE" << std::endl;
        break;
    default:
        std::cout << "ERROR CODE (" << error_code << " ): Undefined" << std::endl;
        break;
    }
}

int release()
{
    DEBUG_FUNCTION_NAME()
    
    /// int WSACleanup();
    ///     操作成功返回值为0;
    ///     否则返回值为SOCKET_ERROR,可以通过调用WSAGetLastError获取错误代码
    ///
    ///     在一个多线程的环境下,WSACleanup()中止了Windows Sockets在所有线程上的操作
    
    if (WSACleanup())
    {
        show_error_code();
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

int init(WSADATA &wsaData)
{
    DEBUG_FUNCTION_NAME()
    
    /// WSAStartup (WSA 是 Windows Sockets API 的缩写)
    /// 该函数必须是使用WSA时调用的第一个函数,用于初始化WSA
    ///
    /// int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
    /// wVersionRequested
    ///     指定调用者可使用的Windows Sockets标准的最高版本。高位字节指定小版本(修订本)号,低位字节指定主版本号
    /// lpWSAData
    ///     指向WSADATA数据结构的指针,用来接收 Windows Sockets 实现的细节
    ///     struct WSAData
    ///     {
    ///         WORD                    wVersion;
    ///         WORD                    wHighVersion;
    ///         unsigned short          iMaxSockets;
    ///         unsigned short          iMaxUdpDg;
    ///         char FAR*               lpVendorInfo;
    ///         char                    szDescription[WSADESCRIPTION_LEN + 1];
    ///         char                    szSystemStatus[WSASYS_STATUS_LEN + 1];
    ///     }
    ///     wVersion:
    ///         Ws2_32.dll库期望调用这使用的Windows Sockets标准的版本。高位字节指定小版本(修订本)号,低位字节指定主版本号
    ///     wHighVersion:
    ///         Ws2_32.dll库支持的Windows Sockets标准的最高版本
    ///     iMaxSockets:
    ///         可以打开SOCKET的最大数量。该参数在WinSock2及以后版本被忽略
    ///     iMaxUdpDg:
    ///         数据报文的最大长度。该参数在WinSock2及以后版本被忽略
    ///     lpVendorInfo
    ///         制造商信息指针。该参数在WinSock2及以后版本被忽略
    ///     szDescription
    ///         以NULL终结的ASCII字符串,指示Windows Sockets实现的描述信息。
    ///     szSystemStatus:
    ///         以NULL终结的ASCII字符串,指示Windows Sockets状态或配置信息
    
    auto wVersionRequested = MAKEWORD(2, 2);
    int major_version = LOBYTE(wVersionRequested);
    int minor_version = HIBYTE(wVersionRequested);
    std::cout << "Expected Major Version: " << major_version << std::endl;
    std::cout << "Expected Minor Version: " << minor_version << std::endl;

    int error_code = WSAStartup(wVersionRequested, &wsaData);
    if (error_code)
    {
        printf("WSAStartup failed with error: %d\n", error_code);
        return EXIT_FAILURE;
    }
    else
    {
        major_version = LOBYTE(wsaData.wVersion);
        minor_version = HIBYTE(wsaData.wVersion);
        std::cout << "Used Major Version: " << major_version << std::endl;
        std::cout << "Used Minor Version: " << minor_version << std::endl;

        major_version = LOBYTE(wsaData.wHighVersion);
        minor_version = HIBYTE(wsaData.wHighVersion);
        std::cout << "High Major Version: " << major_version << std::endl;
        std::cout << "High Minor Version: " << minor_version << std::endl;

        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
        {
            printf("Could not find a usable version of Winsock.dll\n");
            release();
            return EXIT_FAILURE;
        }
        printf("The Winsock 2.2 dll was found okay\n");
    }
    return EXIT_SUCCESS;
}

int get_host_name()
{
    DEBUG_FUNCTION_NAME()
    
    /// int gethostname([out] char *name, [in]  int  namelen);
    ///     获取主机名称。
    ///     如果没有错误发生,返回0;
    ///     否则返回SOCKET_ERROR,并且可通过WSAGetLastError获取一个特定的错误码。
    /// 注意:
    ///     1. 返回的主机名称以null为终结符
    ///     2. 主机名称字符串的长度必须小于等于256
    
    char host_name[256]{0};
    if (SOCKET_ERROR == gethostname(host_name, 256))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }
    else
    {
        std::cout << "host name: " << host_name << std::endl;
    }

    return EXIT_SUCCESS;
}

int create_socket(SOCKET &local_socket, int type)
{
    DEBUG_FUNCTION_NAME()
    
    /// SOCKET WSAAPI socket([in] int af, [in] int type, [in] int protocol);
    ///     根据给定的传输协议创建SOCKET
    /// 参数
    ///     af: 地址族,常用的为AF_INET, AF_INET6
    ///     type: SOCKET 类型:
    ///         SOCK_STREAM     TCP
    ///         SOCK_DGRAM      UDP
    ///         SOCK_RAW        可实现其他协议
    ///     protocol:
    ///         当类型指定为 SOCK_STREAM 或 SOCK_DGRAM,该参数被忽略。当使用SOCK_RAW时,必须指定该参数。可能的值如下:
    ///         IPPROTO_ICMP
    ///         IPPROTO_IGMP
    ///         BTHPROTO_RFCOMM
    ///         IPPROTO_TCP
    ///         IPPROTO_UDP
    ///         IPPROTO_ICMPV6
    ///         IPPROTO_RM
    /// 返回值
    ///     无错误发生,返回一个SOCKET描述符。
    ///     否则返回INVALID_SOCKET,可使用WSAGetLastError()获取特定的错误码。
    
    local_socket = socket(AF_INET, type, 0);
    if (!(local_socket == INVALID_SOCKET))
        return EXIT_SUCCESS;
    show_error_code();
    release();
    return EXIT_FAILURE;
}

int bind_local(SOCKET &socket_server, ADDRESS_FAMILY family, ULONG in_addr, USHORT port)
{
    DEBUG_FUNCTION_NAME()
    
    /// int WSAAPI bind([in] SOCKET s, [in] const sockaddr *name, [in] int namelen);
    ///     绑定本地信息到SOCKET描述符
    /// 参数
    ///     s       SOCKET 描述符
    ///     name    本地地址信息
    ///             如果地址信息中的 name->sin_addr.s_addr 赋值为htonl(INADDR_ANY),则绑定当前主机的所有可用IP
    ///             如果地址信息中的 name->sin_addr.s_addr inet_addr("127.0.0.1"),则只能接受指定IP地址发起的连接或发送的数据
    ///     namelen 本地地址信息的长度
    /// 返回值
    ///     无错误发生,返回0;
    ///     否则,返回 SOCKET_ERROR,可使用WSAGetLastError()获取特定的错误码。
    /// u_long WSAAPI    htonl (u_long hostlong);
    /// u_short WSAAPI   ntohs (u_short netshort);
    ///     将参数类型的值,由主机字节序转换成TCP/IP网络字节序
    
    SOCKADDR_IN local_addr;
    memset(&local_addr, 0, sizeof(local_addr));  // 二选一
    ZeroMemory(&local_addr, sizeof(local_addr)); // 二选一
    local_addr.sin_family = family;
    local_addr.sin_addr.s_addr = in_addr;
    local_addr.sin_port = htons(port);

    if (bind(socket_server, (SOCKADDR *)&local_addr, sizeof(local_addr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

int connect_to_server(SOCKET &socket_client, ADDRESS_FAMILY family, ULONG in_addr, USHORT port)
{
    DEBUG_FUNCTION_NAME()
    
    /// int WSAAPI connect([in] SOCKET s, [in] const sockaddr *name, [in] int namelen);
    ///     本函数用于创建与指定的外部端口的连接。
    /// 参数
    ///     s       待连接的 SOCKET 描述符
    ///     name    待连接的远程地址信息,若该结构中地址域全为零的话,将返回WSAEADDENOTAVAIL
    ///     namelen 地址信息的长度
    /// 注意
    ///     如果在调用该函数之前,未进行本地地址绑定,则系统将自动关联一个本地唯一的地址信息,且设置 SOCKET 为已绑定状态。
    /// 返回值
    ///     无错误发生,返回0
    ///     否则返回 SOCKET_ERROR,一个特定的错误码,可通过WSAGetLastError()进行获取
    
    SOCKADDR_IN remote_addr;
    memset(&remote_addr, 0, sizeof(remote_addr));  // 二选一
    ZeroMemory(&remote_addr, sizeof(remote_addr)); // 二选一
    remote_addr.sin_family = family;
    remote_addr.sin_addr.s_addr = in_addr;
    remote_addr.sin_port = htons(port);

    if (connect(socket_client, (SOCKADDR *)&remote_addr, sizeof(remote_addr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

int start_listen(SOCKET &socket_server)
{
    DEBUG_FUNCTION_NAME()
    
    /// int WSAAPI listen([in] SOCKET s, [in] int backlog);
    ///     开启监听
    /// 参数
    ///     s           SOCKET 描述符
    ///     backlog     待处理连接队列的最大长度。
    ///                 如果设置为 SOMAXCONN,负责 socket 的底层服务提供者会将 backlog 设置为最大合理值。
    ///                 如果设置为 SOMAXCONN_HINT(N)(其中 N 是一个数字),则backlog将为 N,调整到范围 (200, 65535)。
    ///                 请注意,SOMAXCONN_HINT 可用于将 backlog 设置为比 SOMAXCONN 更大的值。
    ///                 SOMAXCONN_HINT 仅受 Microsoft TCP/IP 服务提供商支持。没有获得backlog值的标准规定。
    /// 返回值
    ///     无错误发生,返回0;
    ///     否则,返回 SOCKET_ERROR,可使用WSAGetLastError()获取特定的错误码。
    
    if (listen(socket_server, 5))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

int wait_connect(SOCKET &socket_server, SOCKET &socket_client)
{
    DEBUG_FUNCTION_NAME()
    
    /// SOCKET WSAAPI accept([in] SOCKET s, [out] sockaddr *addr, [in, out] int *addrlen);
    ///     等待客户端连接
    /// 参数
    ///     s           SOCKET 描述符
    ///     addr        已连接的客户端地址信息
        addrlen     已连接的客户端地址信息的长度
    /// 返回值
    ///     无错误发生,返回已连接的客户端的SOCKET描述符,
    ///     否则返回 INVALID_SOCKET,可使用WSAGetLastError()获取特定的错误码。
    
    SOCKADDR_IN client_addr{0};
    int len = sizeof(SOCKADDR);
    memset(&client_addr, 0, len);

    socket_client = accept(socket_server, (SOCKADDR *)&client_addr, &len);
    if (INVALID_SOCKET == socket_client)
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

int recv_message(SOCKET &socket_client, char *buffer, int &recv_length)
{
    DEBUG_FUNCTION_NAME()
    
    /// 无错误,返回接收到的字符串长度。如果连接被正常关闭,返回0
    /// 有错误, 返回 SOCKET_ERROR,可使用WSAGetLastError()获取特定的错误码。
    
    recv_length = recv(socket_client, buffer, BUFFER_SIZE, 0);
    if (recv_length == 0)
    {
        std::cout << "The client is disconnected." << std::endl;
        return EXIT_FAILURE;
    }
    else if (recv_length == SOCKET_ERROR)
    {
        show_error_code();
        closesocket(socket_client);
        return EXIT_FAILURE;
    }
    else if (recv_length < 0 && recv_length > BUFFER_SIZE)
    {
        std::cout << "recvieved length is not correct. length is " << recv_length << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

int send_message(SOCKET &socket, char* buffer, int buffer_length)
{
    DEBUG_FUNCTION_NAME()
    
    /// int WSAAPI send([in] SOCKET s,[in] const char *buf, [in] int len,[in] int flags);
    ///     向已连接的SOCKET上发送数据
    /// 参数
    ///     s  已连接的SOCKET描述符
    ///     buf 待发送数据的首地址
    ///     len 待发送数据的长度
    ///     flags 一组指定的调用标志,可通过位与的方式拼接
    ///         MSG_DONTROUTE   指定数据不受制于路由
    ///         MSG_OOB         发送ODB数据,仅针对流式服务,例如SOCK_STREAM
    /// 返回值
    ///     无错误,返回已发送的字节数
    ///     有错误,返回SOCKET_ERROR,并可通过WSAGetLastError()获取特定的错误码
    
    int length = send(socket, buffer, buffer_length, 0);
    if(SOCKET_ERROR == length)
    {
        show_error_code();
        return EXIT_FAILURE;
    }
    std::cout << "Expected length is " << buffer_length << ", sent length is " << length << std::endl;
    return EXIT_SUCCESS;
}
#endif // CPP_NETWORK_COMMUNICATION_COMMON_H
 

2. CMakeLists.txt

# 指定最低的CMAKE版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.17 FATAL_ERROR)

# 创建项目
PROJECT(cpp_network_communication)

if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
    add_compile_definitions(DEBUG)
endif()

SET(CMAKE_INCLUDE_CURRENT_DIR ON)

# 指定CPLUSPLUS标准
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_C_STANDARD 11)
SET(CMAKE_C_STANDARD_REQUIRED ON)

ADD_EXECUTABLE(tcp_server tcp/tcp_server.cpp)
ADD_EXECUTABLE(tcp_client tcp/tcp_client.cpp)

ADD_EXECUTABLE(udp_server                       udp/udp_server.cpp)
ADD_EXECUTABLE(udp_client                       udp/udp_client.cpp)
ADD_EXECUTABLE(udp_server_boardcast             udp/udp_server_boardcast.cpp)
ADD_EXECUTABLE(udp_client_boardcast             udp/udp_client_boardcast.cpp)
ADD_EXECUTABLE(udp_server_multicast             udp/udp_server_multicast.cpp)
ADD_EXECUTABLE(udp_client_multicast             udp/udp_client_multicast.cpp)
ADD_EXECUTABLE(udp_server_multicast_config      udp/udp_server_multicast_config.cpp)

3. TCP

3.1 服务器(tcp_server.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_server;
    if (create_socket(socket_server, SOCK_STREAM))
        return EXIT_FAILURE;

    if (bind_local(socket_server, AF_INET, htonl(INADDR_ANY), TCP_SERVER_PORT))
        return EXIT_FAILURE;

    if (start_listen(socket_server))
        return EXIT_FAILURE;

    // 单连接
    SOCKET socket_client;
    std::cout << "waiting for client ..." << std::endl;
    if (!wait_connect(socket_server, socket_client))
    {
        char buffer[BUFFER_SIZE + 1]{0};
        for (size_t i = 0; i < COUNT; i++)
        {
            int length = 0;
            std::cout << "waiting for data ..." << std::endl;
            if (!recv_message(socket_client, buffer, length))
            {
                std::cout << "\trecv message: " << buffer << std::endl;
                char *message = "server send client.";
                send_message(socket_client, message, strlen(message) + 1);
            }
            std::cout << "\trecv message: index = " << i << std::endl;
        }
        closesocket(socket_client);
    }

    release();
    system("PAUSE");
    return EXIT_SUCCESS;
}

3.2 客户端(tcp_client.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_client;
    if (create_socket(socket_client, SOCK_STREAM))
        return EXIT_FAILURE;

    // 客户端一般不进行绑定
    // 如果未指定绑定,则在调用connect函数时,系统自动绑定一个本地唯一的端口与IP。此时,每次重新连接,端口号与IP可能不相同(尤其在多IP主机上)
    // 如果进行绑定,则客户端具有固定的IP地址与端口号
    if (bind_local(socket_client, AF_INET, inet_addr("127.0.0.1"), TCP_Client_PORT))
        return EXIT_FAILURE;

    if (connect_to_server(socket_client, AF_INET, inet_addr("127.0.0.1"), TCP_SERVER_PORT))
        return EXIT_FAILURE;

    for (int i = 0; i < COUNT; i++)
    {
        char *message = "client send to server.";
        send_message(socket_client, message, strlen(message) + 1);

        char buffer[BUFFER_SIZE]{0};
        int recv_length;
        recv_message(socket_client, buffer, recv_length);
        std::cout << buffer << std::endl;
        Sleep(1000);
    }

    closesocket(socket_client);

    release();

    system("PAUSE");
    return EXIT_SUCCESS;
}

4. UDP 单播

4.1 服务器/(首次)接收端(udp_server.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_server;
    if (create_socket(socket_server, SOCK_DGRAM))
        return EXIT_FAILURE;

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_server, AF_INET, htonl(INADDR_ANY), UDP_SERVER_PORT))
        return EXIT_FAILURE;

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN client_addr;
        ZeroMemory(&client_addr, sizeof(SOCKADDR_IN));

        int length = sizeof(SOCKADDR);
        char buffer[BUFFER_SIZE + 1]{0};

        int recv_length = recvfrom(socket_server, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&client_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;

        char *message = "server send client.";
        sendto(socket_server, message, strlen(message) + 1, 0, (SOCKADDR *)&client_addr, length);
    }

    closesocket(socket_server);
    release();

    system("PAUSE");
    return 0;
}

4.2 客户端/(首次)发送端(udp_client.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_client;
    if (create_socket(socket_client, SOCK_DGRAM))
        return EXIT_FAILURE;

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_client, AF_INET, inet_addr("127.0.0.1"), UDP_CLIENT_PORT))
        return EXIT_FAILURE;

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN server_addr;
        memset(&server_addr, 0, sizeof(server_addr));

        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(UDP_SERVER_PORT);

        char *message = "client send to server.";
        sendto(socket_client, message, strlen(message) + 1, 0, (SOCKADDR *)&server_addr, sizeof(SOCKADDR));

        int length = sizeof(SOCKADDR);
        char buffer[BUFFER_SIZE + 1]{0};
        int recv_length = recvfrom(socket_client, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&server_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &server_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;

        Sleep(1000);
    }

    closesocket(socket_client);
    system("PAUSE");
    WSACleanup();

    return 0;
}

5. 广播

5.1 服务器/(首次)接收端(udp_server_boardcast.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_server;
    if (create_socket(socket_server, SOCK_DGRAM))
        return EXIT_FAILURE;

    bool bOpt = true;  
    setsockopt(socket_server, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt)); 

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_server, AF_INET, htonl(INADDR_ANY), UDP_SERVER_PORT))
        return EXIT_FAILURE;

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN client_addr;
        ZeroMemory(&client_addr, sizeof(SOCKADDR_IN));

        int length = sizeof(SOCKADDR);
        char buffer[BUFFER_SIZE + 1]{0};

        int recv_length = recvfrom(socket_server, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&client_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;

        char *message = "server send client.";
        sendto(socket_server, message, strlen(message) + 1, 0, (SOCKADDR *)&client_addr, length);
    }

    closesocket(socket_server);
    release();

    system("PAUSE");
    return 0;
}

5.2 客户端/(首次)发送端(udp_client+bpardcast.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_client;
    if (create_socket(socket_client, SOCK_DGRAM))
        return EXIT_FAILURE;

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_client, AF_INET, inet_addr("127.0.0.1"), UDP_CLIENT_PORT))
        return EXIT_FAILURE;

    bool bOpt = true;
    setsockopt(socket_client, SOL_SOCKET, SO_BROADCAST, (char *)&bOpt, sizeof(bOpt));

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN server_addr;
        memset(&server_addr, 0, sizeof(server_addr));

        // 255.255.255.255 受限广播地址,路由器不转发,仅能在本机
        // 主机号全为1,定向广播地址,仅能向当前子网发送
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(UDP_SERVER_PORT);
        server_addr.sin_addr.s_addr = inet_addr("255.255.255.255");

        char *message = "client send to server.";
        sendto(socket_client, message, strlen(message) + 1, 0, (SOCKADDR *)&server_addr, sizeof(SOCKADDR));

        int length = sizeof(SOCKADDR);
        char buffer[BUFFER_SIZE + 1]{0};
        int recv_length = recvfrom(socket_client, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&server_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &server_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;

        Sleep(1000);
    }

    closesocket(socket_client);
    system("PAUSE");
    WSACleanup();

    return 0;
}

6. 多/组播

6.1 服务器/(首次)接收端(udp_server_multicast.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_server;
    if (create_socket(socket_server, SOCK_DGRAM))
        return EXIT_FAILURE;

    // 设置地址复用
    bool is_reuse_addr = true;
    if (setsockopt(socket_server, SOL_SOCKET, SO_REUSEADDR, (char *)&is_reuse_addr, sizeof(is_reuse_addr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_server, AF_INET, htonl(INADDR_ANY), UDP_SERVER_PORT))
        return EXIT_FAILURE;

    // 加入组播
    struct ip_mreq imr;
    imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDRESS);
    imr.imr_interface.s_addr = htonl(INADDR_ANY); // 这种情况下,由接口地址由系统进行选择。在多网卡的环境中,可能会出问题
    // imr.imr_interface.s_addr  = inet_addr(INTERFACE_ADDRESS);   // 直接指定具体网卡

    // 加入多播组
    if (setsockopt(socket_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(imr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN client_addr;
        ZeroMemory(&client_addr, sizeof(SOCKADDR_IN));

        int length = sizeof(SOCKADDR);
        char buffer[BUFFER_SIZE + 1]{0};

        int recv_length = recvfrom(socket_server, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&client_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;

        char *message = "server send to client.";
        sendto(socket_server, message, strlen(message) + 1, 0, (SOCKADDR *)&client_addr, length);
    }

    closesocket(socket_server);
    release();
    system("PAUSE");
    return 0;
}

6.1 客户端/(首次)发送端(udp_client_multicast.cpp)

#include "common/common.h"

int main()
{
    WSADATA wsaData;
    if (init(wsaData))
        return EXIT_FAILURE;

    SOCKET socket_client;
    if (create_socket(socket_client, SOCK_DGRAM))
        return EXIT_FAILURE;

    // 设置地址复用
    bool is_reuse_addr = true;
    if (setsockopt(socket_client, SOL_SOCKET, SO_REUSEADDR, (char *)&is_reuse_addr, sizeof(is_reuse_addr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    // 首次执行接受数据的一方,必须执行绑定;否则,可选
    // 不执行绑定操作,则在第一次调用recvfrom时,系统绑定唯一的地址与端口
    // 执行绑定后,具有唯一的地址与端口
    if (bind_local(socket_client, AF_INET, htonl(INADDR_ANY), UDP_CLIENT_PORT))
        return EXIT_FAILURE;

    // 加入组播
    struct ip_mreq imr;
    imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDRESS);
    imr.imr_interface.s_addr = htonl(INADDR_ANY); // 这种情况下,由接口地址由系统进行选择。在多网卡的环境中,可能会出问题
    // imr.imr_interface.s_addr  = inet_addr(INTERFACE_ADDRESS);   // 直接指定具体网卡

    if (setsockopt(socket_client, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(imr)))
    {
        show_error_code();
        release();
        return EXIT_FAILURE;
    }

    for (size_t i = 0; i < COUNT; i++)
    {
        SOCKADDR_IN server_addr;
        ZeroMemory(&server_addr, sizeof(SOCKADDR_IN));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(MULTICAST_PORT);
        server_addr.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);

        int length = sizeof(SOCKADDR);

        char *message = "client send to server.";
        sendto(socket_client, message, strlen(message) + 1, 0, (SOCKADDR *)&server_addr, length);
        Sleep(1000);

        char buffer[BUFFER_SIZE + 1]{0};
        int recv_length = recvfrom(socket_client, buffer, BUFFER_SIZE, 0, (SOCKADDR *)&server_addr, &length);
        char addr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &server_addr.sin_addr.s_addr, addr, INET_ADDRSTRLEN);
        std::cout << "\tThe client addr is " << addr << std::endl;
        std::cout << "\tThe length of recieved data is " << recv_length << std::endl;
        std::cout << "\tThe recieved data is " << buffer << std::endl;
    }

    closesocket(socket_client);
    release();
    system("PAUSE");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值