当前位置:网站首页>【WinSock】TCP UDP Boardcast Multicast
【WinSock】TCP UDP Boardcast Multicast
2022-07-22 23:11:00 【zhy29563】
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;
}
边栏推荐
- C language decimal number to binary number
- 押注全场景,荣耀胜算几何?
- Flink implements a window accumulation function through processfunction and timer Ontimer
- Strings in C language
- Flink高级API(三)
- 沉淀2年的 Jira 自动化经验分享
- Jedis操作Redis
- Web3流量聚合平台Starfish OS,给玩家元宇宙新范式体验
- Since I used the hiflow scene connector, I don't have to worry about becoming a "drowned chicken" anymore
- 气不过“自愿降薪”,裸辞面字节,四天三面,结局居然这样?
猜你喜欢
随机推荐
Redistemplate pipeline use
Xiaohongshu joins hands with HMS core to enjoy HD vision and grow grass for a better life
一文读懂Elephant Swap的LaaS方案的优势之处
什么是NFT?你不会还不知道吧!
Cloud computing may become a new inflection point in the era? From which point can we see?
嵌入式系统移植【5】——交叉编译工具链
JS regular delete the span tag and the content in the tag
Swift - red modifier
这是一个笑话
How to realize synchronized
吆喝一声就解决了
Redis 持久化操作 (RDB, AOF)
用户登录程序C语言实现
Day011 循环结构中的跳转语句
Shell variables, system predefined variables $home, $pwd, $shell, $user, custom variables, special variables $n, $, $*, [email protected],
odbc excel--2022-07-21
Several ways of QT thread exit
【wepy2.0】的安装
What's the use of volatile
svn: E000022: Can‘t convert string from ‘UTF-8‘ to native encoding 问题解决









