当前位置: 首页 > news >正文

创意网站开发广东色绿色建筑信息平台

创意网站开发,广东色绿色建筑信息平台,淮安建设网站制作,广西建设网站首页目录 一、了解UDP协议 二、了解端口和IP地址 三、套接字概述与Socket的概念 四、Socket的类型 五、 Socket的信息数据结构 六、网络字节序与主机字节序的互相转换 七、地址转换函数 八、UDP网络编程流程及相关函数 socket函数 bind函数 recvfrom函数 sendto函数 …目录 一、了解UDP协议 二、了解端口和IP地址  三、套接字概述与Socket的概念 四、Socket的类型 五、 Socket的信息数据结构 六、网络字节序与主机字节序的互相转换 七、地址转换函数  八、UDP网络编程流程及相关函数 socket函数 bind函数 recvfrom函数  sendto函数  九、创建服务端与客户端之间的网络通信  注意本文的重点为网络编程并不会对TCP、UDP、IP等协议做详细介绍仅做简要说明。 一、了解UDP协议 UDPUser Datagram Protocol用户数据报协议是一个简单的传输层协议用于在网络中进行快速且无连接的数据传输无需像TCP那样通过三次握手来建立一个连接。同时一个UDP应用可同时作为应用的客户或服务器方。 由于UDP不需要建立一个明确的连接因此建立UDP应用要比建立TCP应用简单得多因此我们先以UDP网络编程来导入对网络编程的学习。 下面是UDP协议的是主要特点 主要特点 无连接 UDP 是无连接的协议这意味着在数据传输之前不需要建立和维护连接。每个数据包称为数据报是独立的发送和接收之间不需要进行握手或维护连接状态。 不可靠传输 UDP 不提供可靠的数据传输保证。数据包可能丢失、重复或乱序到达。它不会对数据包的丢失进行重传也不会进行数据的错误校验和修正。应用层需要自行处理这些问题如果需要的话。 面向数据报 UDP 以数据报的形式传输信息每个数据报包含了目标地址和端口号。数据报大小受到限制通常最大为 65,535 字节包括头部和数据部分。 低开销 UDP 的头部开销较小仅 8 字节因为它省略了许多 TCP 中的控制信息如序列号、确认号、流量控制、重传等。这使得 UDP 在网络中具有较低的延迟和开销适用于需要高速传输的应用。 适用于实时应用 由于其低延迟和简单的机制UDP 常用于实时应用如语音通话、视频会议、流媒体等。即使丢失了一些数据包这些应用也可以继续运行而不会对整体体验造成显著影响。 二、了解端口和IP地址  IP 地址和端口号在网络通信中扮演着重要角色它们各自标识了网络中的不同层次。 IP 地址这是一个唯一的标识符用于确定网络中的主机或设备。每台联网的设备都有一个唯一的 IP 地址这样它们才能在网络中相互识别和通信。IP 地址能够标识网络上的唯一设备但并不能唯一标识设备上的具体应用或服务。 端口号这是一个用于标识设备上特定应用或服务的数字。每个设备可以运行多个应用或服务每个服务都通过不同的端口号来区分。端口号的范围为0~65 535一类是由互联网指派名字和号码公司ICANN 负责分配给一些常用的应用程序固定使用的“周知的端口”其值一般为0~1023例如http的端口号是80fp为21ssh为 22telnet为23等还有一类是用户自己定义的通常是大于1024的整型值。端口号与 IP 地址一起能够唯一标识网络上的具体服务或应用。 因此IP 地址和端口号的组合可以唯一标识网络上的具体服务或应用进程。例如一个 IP 地址为 192.168.1.1 的设备上的 HTTP 服务可以通过 192.168.1.1:80 来访问。 三、套接字概述与Socket的概念 套接字是操作系统内核中的一个数据结构它是网络中的节点进行相互通信的门户是网络进程的ID。网络通信归根到底还是进程间的通信(不同计算机上的进程间通信)。在网络中每一个节点(计算机或路由)都有一个网络地址也就是IP地址。在两个进程进行通信时首先要确定各自所在的网络节点的网络地址。但是网络地址只能确定进程所在的计算机而一台计算机上很可能同时运行着多个进程所以仅凭网络地址还不能确定到底要和网络中的哪一个进程进行通信,因此套接字中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中一个端口号一次只能分配给一个进程。也就是说在一台计算机中端口号和进程之间是一一对应的关系所以使用端口号和网络地址的组合可以唯一地确定整个网络中的一个网络进程。 例如假设网络中某一台计算机的IP地址为10.92.20.160操作系统分配给计算机中某应用程序进程的端口号为1500则此时10.92.20.1601500就构成了一个套接字。 Linux中的网络编程是通过Socket来进行的。Socket 是一种特殊的IO接口也是一种文件描述符。它是一种常用的进程之间的通信机制通过它不仅能实现本地机器上的进程之间的通信而且通过网络能够在不同机器上的进程之间进行通信。 每一个Socket都用一个半相关描述“{协议、本地地址、本地端口}”来表示:一个完整的套接字则用一个相关描述“(协议、本地地址、本地端口、远程地址、远程端口)”来表示。Socket也有一个类似于打开文件的函数调用该函数返回一个整型的Socket描述符随后的连接建立、数据传输等操作都是通过Socket来实现的。套接字socket是计算机网络中用于进行通信的一个端点。 四、Socket的类型 流式Socket(SOCK STREAM)用于TCP通信。流式套接字提供可靠的、面向连接的通信流它使用传输控制协议TCP从而保证数据传输的正确性和顺序性。 数据报Socket(SOCK DGRAM)用于UDP通信。数据报套接字定义了一种无连接的服务数据通过相互独立的报文进行传输是无序的并且不保证是可靠、无差错的它使用数据报协议UDP。 原始Socket(SOCK RAW)用于新的网络协议实现的测试等。原始套接字允许对底层协数据报协议 UDP。议如PP或ICMP进行直接访问它功能强大但使用较为不便主要用于一些协议的开发。 五、 Socket的信息数据结构 socket 常见 API应用程序编程接口 // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器) int bind(int socket, const struct sockaddr* address, socklen_t address_len);// 开始监听 socket (TCP, 服务器) int listen(int socket, int backlog);// 接收请求 (TCP, 服务器) int accept(int socket, struct sockaddr* address, socklen_t* address_len);// 建立连接 (TCP, 客户端) int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);socket API 是一层抽象的网络编程接口适用于各种底层网络协议如 IPv4、IPv6UNIX Domain Socket。 然而,各种网络协议的地址格式并不相同这些地址格式需要通过 Socket API 进行适配。 •IPv4 和 IPv6 的地址格式定义在 netinet/in.h 中IPv4 地址用 sockaddr_in 结构体表示包括 16 位地址类型16 位端口号和 32 位 IP 地址。 • IPv4、 IPv6 地址类型分别定义为常数 AF_INET、 AF_INET6。这样只要取得某种 sockaddr 结构体的首地址不需要知道具体是哪种类型的 sockaddr 结构体就可以根据地址类型字段确定结构体中的内容。 • socket API 可以都用 struct sockaddr *类型表示在使用的时候需要强制转化成sockaddr_in 这样的好处是程序的通用性可以接收 IPv4, IPv6, 以及 UNIX Domain Socket 各种类型的 sockaddr 结构体指针做为参数。 1. 地址类型 在 netinet/in.h 头文件中IPv4 和 IPv6 的地址格式分别用 struct sockaddr_in 和 struct sockaddr_in6 结构体表示。sockaddr 结构体是一个通用的地址结构体用于在编程中处理不同的地址类型 IPv4 地址 (struct sockaddr_in) struct sockaddr_in {short int sin_family; // 地址族通常为 AF_INETunsigned short int sin_port; // 端口号struct in_addr sin_addr; // IP 地址char sin_zero[8]; // 填充以保证结构体大小为 16 字节 };struct in_addr {in_addr_t s_addr; // 32位 IPv4 地址网络字节序in_addr_t (unsigned long int ) };IPv6 地址 (struct sockaddr_in6) struct sockaddr_in6 {u_int16_t sin6_family; // 地址族通常为 AF_INET6u_int16_t sin6_port; // 端口号u_int32_t sin6_flowinfo; // 流量信息struct in6_addr sin6_addr; // IPv6 地址u_int32_t sin6_scope_id; // 作用域 ID };struct in6_addr {unsigned char s6_addr[16]; // IPv6 地址的 16 字节表示网络字节序 };2. 地址族常量 地址族常量定义了不同的协议族 AF_INET表示 IPv4 地址族。AF_INET6表示 IPv6 地址族。AF_UNIX表示 UNIX Domain Socket 地址族。 这些常量用于指示 sockaddr 结构体中存储的地址类型使得程序可以在运行时根据地址族来选择和处理合适的结构体。 3. 使用 struct sockaddr struct sockaddr 是一个通用的结构体用于存储所有类型的地址。实际使用时程序可以通过强制类型转换将 struct sockaddr 转换为具体的地址结构体如 struct sockaddr_in 或 struct sockaddr_in6以访问特定的字段 // 创建一个 IPv4 套接字 int sockfd socket(AF_INET, SOCK_STREAM, 0);// 定义 IPv4 地址 struct sockaddr_in addr; addr.sin_family AF_INET; addr.sin_port htons(12345); // 设置端口号 inet_pton(AF_INET, 192.168.1.1, addr.sin_addr); // 设置 IP 地址// 使用 struct sockaddr * 作为参数 bind(sockfd, (struct sockaddr *)addr, sizeof(addr));// 定义 IPv6 地址 struct sockaddr_in6 addr6; addr6.sin6_family AF_INET6; addr6.sin6_port htons(12345); // 设置端口号 inet_pton(AF_INET6, 2001:0db8:85a3:0000:0000:8a2e:0370:7334, addr6.sin6_addr); // 设置 IPv6 地址// 使用 struct sockaddr * 作为参数 bind(sockfd, (struct sockaddr *)addr6, sizeof(addr6)); 六、网络字节序与主机字节序的互相转换 网络序列和主机序列的转化涉及到数据在网络上传输时的字节顺序问题。 字节序Endianess 大端序Big-Endian数据的高位字节存在低地址处低位字节在高地址处。小端序Little-Endian数据的低位字节存在低地址处高位字节在高地址处。 不同的计算机系统可能使用不同的字节序来存储数据这会导致同一数据在不同系统之间的表示方式不同。例如一台使用大端序的机器和一台使用小端序的机器在数据传输时会有不同的解释。 网络序列和主机序列 网络序列网络协议通常采用大端序来表示数据这种顺序被称为网络字节序。网络字节序是为了保证不同平台之间的数据能够一致地解释。 主机序列是指计算机本身使用的字节序可能是大端序也可能是小端序。 转化的必要性 在网络通信中数据从一个主机发送到另一个主机时数据的字节顺序必须统一以确保接收方能够正确解析数据。由于不同主机可能使用不同的字节序数据在网络上传输时需要进行转化 从主机序列到网络序列在发送数据之前发送方需要将数据从主机字节序转化为网络字节序。这通常使用系统提供的函数来实现如 htonl()将主机字节序的长整型转化为网络字节序、htons()将主机字节序的短整型转化为网络字节序。 从网络序列到主机序列在接收数据时接收方需要将数据从网络字节序转化为主机字节序。这通常使用函数如 ntohl()将网络字节序的长整型转化为主机字节序、ntohs()将网络字节序的短整型转化为主机字节序。 示例 假设一个大端序主机发送一个整数 0x12345678十进制的305419896在网络中数据以大端序 0x12 0x34 0x56 0x78 传输。如果接收方是小端序主机它接收到的字节顺序是 0x78 0x56 0x34 0x12需要进行转化以得到正确的整数值 0x12345678。 字节序转换相关函数 这些函数在 arpa/inet.h 头文件中声明 以下函数中h代表host主机n代表network网络s代表short16位字节序l代表long32位字节序。 1. htons 函数原型 uint16_t htons(uint16_t hostshort);功能将主机字节序的 uint16_t 类型数值转换为网络字节序。 2. ntohs 函数原型 uint16_t ntohs(uint16_t netshort);功能将网络字节序的 uint16_t 类型数值转换为主机字节序。 3. htonl 函数原型 uint32_t htonl(uint32_t hostlong);功能将主机字节序的 uint32_t 类型数值转换为网络字节序。 4. ntohl 函数原型 uint32_t ntohl(uint32_t netlong);功能将网络字节序的 uint32_t 类型数值转换为主机字节序。 以下是一个示例程序演示了如何使用以上函数进行字节序转换 #include stdio.h #include stdint.h #include arpa/inet.hint main() {uint16_t port 8080;uint32_t ip 3232235776; // 192.168.1.0// 主机到网络字节序转换uint16_t net_port htons(port);uint32_t net_ip htonl(ip);// 网络字节序到主机字节序转换uint16_t host_port ntohs(net_port);uint32_t host_ip ntohl(net_ip);printf(Original port: %u\n, port);printf(Network port: %u\n, net_port);printf(Host port: %u\n, host_port);printf(Original IP: %u\n, ip);printf(Network IP: %u\n, net_ip);printf(Host IP: %u\n, host_ip);return 0; }在上述示例中 htons 将主机字节序的端口号 port 转换为网络字节序。ntohs 将网络字节序的端口号 net_port 转换回主机字节序。htonl 将主机字节序的 IP 地址 ip 转换为网络字节序。ntohl 将网络字节序的 IP 地址 net_ip 转换回主机字节序。 七、地址转换函数  地址转换函数用于在不同格式之间转换 IP 地址。它们在网络编程中非常重要特别是在处理 IP 地址和域名时。以下是常见的地址转换函数的详细介绍 1. inet_addr 函数原型 in_addr_t inet_addr(const char *cp);功能将点分十进制的 IP 地址如 “192.168.1.1”转换为网络字节序的 in_addr_t 类型通常为 uint32_t的二进制格式。 返回值成功时返回 IP 地址的网络字节序格式失败时返回 INADDR_NONE通常为 0xFFFFFFFF。 示例 #include stdio.h #include arpa/inet.hint main() {const char *ip_str 192.168.1.1;in_addr_t ip inet_addr(ip_str);if (ip INADDR_NONE) {printf(Invalid IP address\n);} else {printf(IP address in network byte order: %u\n, ip);}return 0; }2. inet_ntoa 函数原型 char *inet_ntoa(struct in_addr in);功能将网络字节序的 struct in_addr 结构体转换为点分十进制的 IP 地址字符串如 “192.168.1.1”。 返回值返回指向字符串的指针。如果失败返回 NULL。需要注意返回的字符串是静态分配的因此不适合多线程环境或并发使用。 示例 #include stdio.h #include arpa/inet.hint main() {struct in_addr addr;addr.s_addr htonl(0xC0A80101); // 192.168.1.1char *ip_str inet_ntoa(addr);printf(IP address: %s\n, ip_str);return 0; }3. inet_pton 函数原型 int inet_pton(int af, const char *src, void *dst);功能将 IP 地址的文本表示形式如 “192.168.1.1” 或 “2001:db8::1”转换为网络字节序的二进制格式。 参数 af地址族通常为 AF_INETIPv4或 AF_INET6IPv6。src指向包含 IP 地址的字符串的指针。dst指向存储转换结果的内存地址。 返回值成功时返回 1无效地址时返回 0出错时返回 -1。 示例 #include stdio.h #include arpa/inet.hint main() {struct in_addr addr;if (inet_pton(AF_INET, 192.168.1.1, addr) 1) {printf(Address converted successfully\n);} else {printf(Invalid address\n);}return 0; }4. inet_ntop 函数原型 const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);功能将网络字节序的二进制格式如 struct in_addr 或 struct in6_addr转换为 IP 地址的文本表示形式。 参数 af地址族通常为 AF_INETIPv4或 AF_INET6IPv6。src指向包含网络字节序 IP 地址的内存地址。dst指向存储转换结果的缓冲区。size缓冲区大小。 返回值成功时返回指向结果字符串的指针失败时返回 NULL。 示例 #include stdio.h #include arpa/inet.hint main() {struct in_addr addr;addr.s_addr htonl(0xC0A80101); // 192.168.1.1char ip_str[INET_ADDRSTRLEN];if (inet_ntop(AF_INET, addr, ip_str, sizeof(ip_str))) {printf(IP address: %s\n, ip_str);} else {printf(Conversion failed\n);}return 0; }总结 这些地址转换函数使得在处理 IP 地址时可以在字符串表示和网络字节序之间进行转换。可以简化对 IP 地址的操作并保证在网络通信中使用的地址格式正确。  八、UDP网络编程流程及相关函数 socket函数 函数功能用于创建一个新的套接字套接字socket是计算机网络中用于进行通信的一个端点。 1. 函数原型 在不同的编程语言和平台中socket 函数的原型可能有所不同但在 C 语言和 POSIX 标准下它的原型通常是 int socket(int domain, int type, int protocol);2. 参数说明 domain指定套接字的域或协议族常见的有 AF_INETIPv4 网络协议。AF_INET6IPv6 网络协议。AF_UNIX用于本地进程间通信的 Unix 域套接字。 type指定套接字的类型常见的有 SOCK_STREAM面向连接的流式套接字通常用于 TCP 协议。SOCK_DGRAM数据报套接字通常用于 UDP 协议。SOCK_RAW原始套接字通常用于访问底层网络协议。 protocol指定协议类型。一般情况下你可以设置为 0这样系统会根据 domain 和 type 自动选择合适的协议。例如对于 SOCK_STREAM 类型的套接字系统会默认使用 TCP 协议对于 SOCK_DGRAM 类型的套接字系统会默认使用 UDP 协议。 3. 返回值 成功时socket 函数返回一个非负整数这个整数是套接字的描述符。失败时返回 -1并设置 errno 以指示错误类型。 4. 错误处理 当 socket 函数失败时你可以通过 errno 获取错误代码。常见的错误代码有 EAFNOSUPPORT不支持指定的地址族。EINVAL提供了无效的参数。PROTONOSUPPORT不支持指定的协议。 5. 示例代码 以下是一个简单的 C 语言示例演示如何创建一个 IPv4 和 UDP 的套接字 #include stdio.h #include stdlib.h #include unistd.h #include sys/socket.h #include netinet/in.hint main() {int sockfd;// 创建套接字sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0) {perror(socket);exit(EXIT_FAILURE);}printf(Socket created successfully.\n);// 关闭套接字close(sockfd);return 0; } bind函数 函数功能bind 函数在网络编程中用于将一个套接字socket与一个本地地址IP 地址和端口绑定起来。 1. 函数原型 在 C 语言和 POSIX 标准下bind 函数的原型如下 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);2. 参数说明 sockfd套接字描述符这是由 socket 函数创建并返回的套接字。 addr指向 sockaddr 结构体的指针用于指定要绑定的本地地址。通常会使用具体的结构体如 sockaddr_in用于 IPv4 地址或 sockaddr_in6用于 IPv6 地址。 addrlenaddr 指向的地址结构体的长度。对于 sockaddr_in通常是 sizeof(struct sockaddr_in)。 3. sockaddr 结构体 sockaddr_in用于 IPv4 struct sockaddr_in {sa_family_t sin_family; // 地址族通常为 AF_INETuint16_t sin_port; // 端口号网络字节顺序struct in_addr sin_addr; // IP 地址 };struct in_addr {uint32_t s_addr; // IP 地址网络字节顺序 };sockaddr_in6用于 IPv6 struct sockaddr_in6 {sa_family_t sin6_family; // 地址族通常为 AF_INET6uint16_t sin6_port; // 端口号网络字节顺序uint32_t sin6_flowinfo; // 流量信息struct in6_addr sin6_addr; // IPv6 地址uint32_t sin6_scope_id; // 范围 ID };struct in6_addr {unsigned char s6_addr[16]; // IPv6 地址 };4. 返回值 成功时返回 0。失败时返回 -1并设置 errno 以指示错误类型。 5. 错误处理 常见的错误代码包括 EADDRINUSE地址已经在使用中通常是端口被占用。EADDRNOTAVAIL提供的地址在本地不可用。EINVAL提供了无效的参数。ENOTSOCK描述符不是一个套接字。 6. 示例代码 以下是一个简单的 C 语言示例演示如何将一个套接字绑定到一个特定的本地地址和端口 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/types.h #include sys/socket.hint main() {int sockfd;struct sockaddr_in server_addr;// 创建 UDP 套接字sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0) {perror(socket);exit(EXIT_FAILURE);}// 配置服务器地址memset(server_addr, 0, sizeof(server_addr));server_addr.sin_family AF_INET;server_addr.sin_addr.s_addr INADDR_ANY; // 监听所有可用接口server_addr.sin_port htons(12345); // 设置端口号转换为网络字节顺序// 绑定套接字到本地地址if (bind(sockfd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) {perror(bind);close(sockfd);exit(EXIT_FAILURE);}printf(UDP socket successfully bound to port 12345.\n);// 关闭套接字close(sockfd);return 0; }7. 使用场景 bind 函数通常在服务器端使用绑定套接字到特定的 IP 地址和端口以便监听来自客户端的连接请求。在客户端通常不需要显式地调用 bind除非你需要绑定到特定的本地地址和端口。 recvfrom函数  函数功能recvfrom函数是用于接收数据的系统调用适用于面向无连接的协议如UDP用户数据报协议。 1、函数原型 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);2、参数解释 sockfd已经创建并绑定的套接字的文件描述符。buf指向缓冲区的指针用于存储接收到的数据。len缓冲区buf的大小以字节为单位。flags标志位用于修改recvfrom的行为通常情况下设置为0默认为阻塞读取。其他参数设置例如MSG_DONTWAIT表示非阻塞模式MSG_WAITALL表示等待直到接收到指定长度的数据。src_addr指向sockaddr结构体的指针用于存储发送方的地址信息。如果不需要获取发送方的地址可以设置为NULL。addrlen指向整型的指针用于指定src_addr结构体的大小。在调用recvfrom之前应将其设置为src_addr结构体的大小调用后它将被设置为新接收到的地址的实际大小。 3、返回值 成功时recvfrom返回接收到的字节数。如果连接被对方优雅地关闭则返回0。如果发生错误返回-1并设置全局变量errno来指示错误的原因。 4、注意事项 使用recvfrom时需要处理可能出现的各种错误情况。例如如果套接字处于非阻塞模式并且没有数据可读recvfrom可能会返回EAGAIN或EWOULDBLOCK错误。recvfrom函数可以同时接收数据并获取数据发送方的地址信息这是它与recv函数的一个主要区别。对于UDP协议的套接字由于UDP数据包可能会被分片因此需要多次读取才能将一个完整的数据包接收完毕。 示例代码 以下是一个简单的 UDP 服务器示例展示如何使用 recvfrom 函数接收数据并获取发送方的地址信息 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/types.h #include sys/socket.h#define PORT 12345 #define BUF_SIZE 1024int main() {int sockfd;struct sockaddr_in server_addr, client_addr;socklen_t client_len;char buf[BUF_SIZE];ssize_t recv_len;// 创建 UDP 套接字sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0) {perror(socket);exit(EXIT_FAILURE);}// 配置服务器地址memset(server_addr, 0, sizeof(server_addr));server_addr.sin_family AF_INET;server_addr.sin_addr.s_addr INADDR_ANY; // 监听所有可用接口server_addr.sin_port htons(PORT); // 设置端口号// 绑定套接字到本地地址if (bind(sockfd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) {perror(bind);close(sockfd);exit(EXIT_FAILURE);}printf(UDP server listening on port %d\n, PORT);// 接收数据client_len sizeof(client_addr);recv_len recvfrom(sockfd, buf, BUF_SIZE - 1, 0, (struct sockaddr *)client_addr, client_len);if (recv_len 0) {perror(recvfrom);close(sockfd);exit(EXIT_FAILURE);}// Null-terminate and print received databuf[recv_len] \0;printf(Received message: %s\n, buf);printf(From address: %s\n, inet_ntoa(client_addr.sin_addr));printf(From port: %d\n, ntohs(client_addr.sin_port));// 关闭套接字close(sockfd);return 0; }代码说明 创建套接字使用 socket 函数创建一个 UDP 套接字。配置服务器地址设置服务器地址信息包括协议族AF_INET、地址INADDR_ANY、端口号使用 htons 转换为网络字节顺序。绑定套接字将套接字绑定到指定的本地地址和端口。接收数据 使用 recvfrom 函数接收数据。recvfrom 填充 buf 缓冲区并将客户端的地址信息填充到 client_addr 结构体中。recv_len 保存接收到的字节数。处理数据 将接收到的数据缓冲区 buf 以 null 字符终止并打印出来。使用 inet_ntoa 函数将客户端的 IP 地址转换为字符串格式。使用 ntohs 函数将客户端的端口号转换为主机字节顺序。关闭套接字结束时关闭套接字以释放资源。 sendto函数  函数功能sendto 函数是网络编程中用于发送数据的一个函数通常用于 UDP 协议。它的主要功能是将数据包发送到指定的目标地址。sendto 函数常用于 Socket 编程特别是在处理无连接的 UDP 套接字时。 函数原型 在不同的编程语言和平台中sendto 的函数原型可能有所不同但在 C 语言的 POSIX 标准中sendto 的函数原型如下 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);参数说明 sockfd: 套接字描述符是通过 socket 函数创建的套接字的文件描述符。该套接字应当是一个有效的、已创建的 UDP 套接字。 buf: 指向要发送数据的缓冲区的指针。这个缓冲区中的数据会被发送到目标地址。 len: 要发送的数据的字节数。它指定了 buf 中的有效数据长度。 flags: 发送数据的标志。通常情况下这个参数设置为 0但也可以使用不同的标志来控制发送行为如 MSG_CONFIRM, MSG_DONTROUTE 等。 dest_addr: 指向 sockaddr 结构体的指针这个结构体包含了目标地址的信息。对于 UDP 套接字这通常是一个 sockaddr_in 结构体用于指定目标主机的 IP 地址和端口号。 addrlen: dest_addr 指向的地址结构体的长度。对于 sockaddr_in 结构体通常是 sizeof(struct sockaddr_in)。 返回值 成功时sendto 返回发送的字节数即 len如果数据包部分发送成功那么返回值可能小于 len。失败时返回 -1并设置 errno 以指示错误原因。 示例代码 以下是一个使用 sendto 函数的简单示例演示了如何通过 UDP 发送数据 #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.hint main() {int sockfd;struct sockaddr_in servaddr;char *message Hello, UDP server!;// 创建 UDP 套接字sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0) {perror(socket creation failed);exit(EXIT_FAILURE);}// 配置目标地址memset(servaddr, 0, sizeof(servaddr));servaddr.sin_family AF_INET;servaddr.sin_port htons(12345); // 目标端口servaddr.sin_addr.s_addr inet_addr(127.0.0.1); // 目标 IP 地址// 发送数据ssize_t n sendto(sockfd, message, strlen(message), 0,(struct sockaddr *)servaddr, sizeof(servaddr));if (n 0) {perror(sendto failed);close(sockfd);exit(EXIT_FAILURE);}printf(Sent %zd bytes to server\n, n);// 关闭套接字close(sockfd);return 0; }上述代码创建了一个 UDP 套接字配置了目标地址并通过 sendto 函数将一条消息发送到指定的 IP 和端口。 九、创建服务端与客户端之间的网络通信  为什么客户端不需要bind  临时性端口客户端通常由操作系统自动为其分配一个临时的本地端口。客户端只需要指定目标服务器的 IP 地址和端口号操作系统会为客户端选择一个合适的本地IP地址及本地端口来完成通信。 简单的连接模型客户端主要是发起连接请求并接收响应。由于客户端是发起方网络栈会自动处理本地端口的分配和管理。 基于对上述知识的了解以及UDP编程相关函数的认识接下来我们依照上述UDP编程流程图来创建一个简易的服务端与客户端之间的网络通信。 首先我们需要封装一个服务端类该类的功能包括初始化服务端、启动服务端、关闭服务端而服务端通常是不允许被拷贝的所以我们需要禁止编译器默认生成拷贝构造和赋值运算符重载。 服务端方法设计接口如下  #define BUFFER_SIZE 1024const int temp_socketfd -1; const std::string temp_IP 127.0.0.1; //回环地址,表示计算机自身的网络接口,作为测试使用 const uint16_t temp_port 8888; //自行设置的端口号大小必须在1024~65535之间enum {SOCKET_ERROR 1,BIND_ERROR,RECVFROM_ERROR,SENDTO_ERROR };class Socket_Server { private:int _socketfd; //套接字描述符bool _isrunning;//服务器运行标志uint16_t _port; //16位端口号std::string _ip;//服务器IP地址【注意暂时设置后续可以进行更改】public:Socket_Server(const Socket_Server) delete;Socket_Server operator(const Socket_Server) delete;//构造函数Socket_Server(const std::string IP temp_IP, const uint16_t port temp_port):_socketfd(temp_socketfd), _isrunning(false), _port(port), _ip(IP){}//初始化服务器void Init(){//1、使用scoket函数建立套接字。AF_INET代表IPv4网络协议,SOCK_DGRAM指定为数据报套接字通常用于 UDP 协议。//2、使用bind 函数将一个套接字socket与一个本地地址IP 地址和端口绑定起来。}//启动服务器void Start(){//1、保持服务器不退出使用recvfrom接收客户端信息//2、信息接收成功后使用sendto向客户端回复信息}//析构函数~Socket_Server(){//1、关闭套接字描述符} }; 1、初始化服务器 //初始化服务器void Init(){//1、使用scoket函数建立套接字。AF_INET代表IPv4网络协议,SOCK_DGRAM指定为数据报套接字通常用于 UDP 协议。_socketfd ::socket(AF_INET, SOCK_DGRAM, 0);//第三个参数指定为0即可标识根据前面两个参数选择指定协议if(_socketfd 0){perror(Create Socket False!!!);exit(SOCKET_ERROR);}std::cout Socket Success!!! std::endl;//2、使用bind 函数将一个套接字socket与一个本地地址IP 地址和端口绑定起来。struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;//地址族设置为IPv4local.sin_port htons(_port);//16位主机序端口号转换为网络序local.sin_addr.s_addr inet_addr(_ip.c_str());//local.sin_addr.s_addr INADDR_ANY; //也可以这样设置后续单独解释int ret ::bind(_socketfd, (struct sockaddr*)local, sizeof(local));if(ret -1){perror(Bind IP And Port False!!!);exit(BIND_ERROR);}std::cout Bind Success!!! std::endl;} INADDR_ANY宏 INADDR_ANY 的值为 0.0.0.0表示任意地址。它的具体定义通常是一个 32 位的整数表示 IPv4 地址中的任意地址。 在进行套接字绑定IP地址的操作时我们可以不绑定具体的地址。 当绑定具体的地址时服务器仅接受从该特定 IP 地址发来的连接请求。 当不绑定到具体的 IP 地址而使用INADDR_ANY 时服务器会监听来自主机所有网络接口的连接请求。 local.sin_addr.s_addr INADDR_ANY; 对于一台主机来讲它可能有多个网卡网络接口可能是物理网卡或虚拟网卡。每个网卡都有各自的独立的IP地址。当客户端想访问服务端时它需要拿到服务端进程绑定的IP地址和端口号。         但是对于一台主机而言服务端进程的端口号是唯一的但是IP地址却不止一个。当需要接收来自多个接口的连接请求时将绑定地址设置为INADDR_ANY 如此客户端发送的信息只要发送的目的IP地址属于服务端主机并且目的端口号也是服务端进程的端口号那么服务端就可以接收到向这台主机发送的所有的数据和请求。 2、运行服务器 //启动服务器void Start(){//1、保持服务器不退出使用recvfrom接收客户端信息并使用sendto向客户端回复信息_isrunning true;char buffer[BUFFER_SIZE];memset(buffer, 0, sizeof(buffer));while(_isrunning){struct sockaddr_in from_client;memset(from_client, 0, sizeof(from_client));socklen_t client_len sizeof(from_client);size_t ret ::recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)from_client, client_len);if(ret 0){_isrunning false;std::cout Server Exit!!! std::endl;}else if(ret 0){perror(Recvfrom False!!!); exit(RECVFROM_ERROR);}else{buffer[ret] \0;//将接收的信息打印出来:std::string client_ip_addr inet_ntoa(from_client.sin_addr);std::string inf Receive Information From Client IP client_ip_addr : buffer;std::cout inf std::endl;//向客户端反馈已经收到信息std::string res Server Echo : ;res buffer;size_t ret ::sendto(_socketfd, res.c_str(), res.size(), 0, (struct sockaddr*)from_client, client_len);if(ret -1){perror(Sendto False!!!); exit(SENDTO_ERROR);}}}} 3、关闭服务器 在main函数中对服务器对象的管理我们可以采取两种方式1、使用智能指针管控当服务端接到信号进行退出时智能指针会及时合理释放服务端对象占用的内存空间。2、在栈上创建对象当程序退出后函数的栈帧销毁服务器对象资源也随之被清理。 我们要做的就是确保对象销毁时将套接字的描述符进行关闭以确保文件描述符表中的无用资源被清理这一步在析构函数中进行 //析构函数~Socket_Server(){//关闭套接字描述符if(_socketfd 0)::close(_socketfd);} 需要注意的是套接字描述符实际也属于文件描述符同样遵守文件描述符的规则即从文件描述符表中最小的空闲位置开始为其分配文件描述符。由于文件描述符表是一个指针数组而文件描述符实际是这个数组的下标这也注定了套接字描述符不会小于0 客户端代码  #pragma once #include string #include iostream #include signal.h #include sys/types.h #include sys/socket.h #include cerrno #include cstring #include netinet/in.h #include arpa/inet.h#define BUFFER_SIZE 1024const int temp_socketfd -1; const std::string temp_IP 127.0.0.1; //回环地址,表示计算机自身的网络接口,作为测试使用 const uint16_t temp_port 8888; //自行设置的端口号大小必须在1024~65535之间enum{SOCKET_ERROR 1,BIND_ERROR,RECVFROM_ERROR,SENDTO_ERROR };class Client_Server { private:int _socketfd; //套接字描述符bool _isrunning;//服务器运行标志uint16_t server_port; //服务器16位端口号std::string server_ip;//服务器IP地址【注意暂时设置后续有更改】public:Client_Server(const Client_Server) delete;Client_Server operator(const Client_Server) delete;//构造函数Client_Server(const std::string IP temp_IP, const uint16_t port temp_port):_socketfd(temp_socketfd), _isrunning(false), server_port(port), server_ip(IP){}//初始化客户端void Init(){//1、使用scoket函数建立套接字。AF_INET代表IPv4网络协议,SOCK_DGRAM指定为数据报套接字通常用于 UDP 协议。_socketfd ::socket(AF_INET, SOCK_DGRAM, 0);//第三个参数指定为0即可标识根据前面两个参数选择指定协议if(_socketfd 0){perror(Client Create Socket False!!!);exit(SOCKET_ERROR);}std::cout Socket Success!!! std::endl;}//启动服务器void Start(){_isrunning true;char buffer[BUFFER_SIZE];memset(buffer, 0, sizeof(buffer));while(_isrunning){//1、客户端输入信息std::cout Please Enter : ;std::string enter;getline(std::cin, enter);//2、设置服务端的套接字的信息结构使用sendto向目的IP和端口号发送信息struct sockaddr_in server_sock;memset(server_sock, 0, sizeof(server_sock));server_sock.sin_family AF_INET;//地址族设置为IPv4server_sock.sin_port htons(server_port);//16位主机序端口号转换为网络序server_sock.sin_addr.s_addr inet_addr(server_ip.c_str());size_t send_num sendto(_socketfd, enter.c_str(), enter.size(), 0, (struct sockaddr*)server_sock, sizeof(server_sock));if(send_num 0){perror(Client Sendto False!!!);exit(SENDTO_ERROR);}//3、使用recvfrom获取服务端的反馈信息并获取服务端的套接字的信息结构struct sockaddr_in from_server;memset(from_server, 0, sizeof(from_server));socklen_t len sizeof(from_server);char buffer[BUFFER_SIZE];size_t recv_num recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)from_server, len);if(recv_num 0){perror(Client Recvfrom False!!!);exit(RECVFROM_ERROR);}buffer[recv_num] \0;//4、将接收的信息打印出来:std::string client_ip_addr inet_ntoa(from_server.sin_addr);std::string inf Receive Information From Server IP client_ip_addr : buffer;std::cout inf std::endl;}}//析构函数~Client_Server(){//关闭套接字描述符if(_socketfd 0)::close(_socketfd);} }; 因为客户端向服务端发送信息时需要指定服务端的IP地址和端口号因此在客户端的main函数中可以采取命令行参数的方式从命令行输入中获取用户主动输入的IP地址和端口号。 由于格式不同使用相应的格式转换函数进行转换即可。 #include UDP_Client.hppint main(int argc, char* argv[]) {if(argc 3){std::cerr Usage: argv[0] server-ip and server-port not provided std::endl;exit(0);}Client_Server client(argv[1], std::stoi(argv[2]));client.Init();client.Start();return 0; } Makefile  .PHONY:all all:server clientserver:Server_Main.ccg -o $ $^ -stdc14client:Client_Main.ccg -o $ $^ -stdc14.PHONY:clean clean:rm -f server client 效果展示
文章转载自:
http://www.morning.mdwlg.cn.gov.cn.mdwlg.cn
http://www.morning.qphcq.cn.gov.cn.qphcq.cn
http://www.morning.gsqw.cn.gov.cn.gsqw.cn
http://www.morning.qsszq.cn.gov.cn.qsszq.cn
http://www.morning.qsbcg.cn.gov.cn.qsbcg.cn
http://www.morning.stsnf.cn.gov.cn.stsnf.cn
http://www.morning.sjftk.cn.gov.cn.sjftk.cn
http://www.morning.baguiwei.com.gov.cn.baguiwei.com
http://www.morning.jzlfq.cn.gov.cn.jzlfq.cn
http://www.morning.qscsy.cn.gov.cn.qscsy.cn
http://www.morning.ykklw.cn.gov.cn.ykklw.cn
http://www.morning.zryf.cn.gov.cn.zryf.cn
http://www.morning.jtqxs.cn.gov.cn.jtqxs.cn
http://www.morning.nxstj.cn.gov.cn.nxstj.cn
http://www.morning.hpxxq.cn.gov.cn.hpxxq.cn
http://www.morning.qwnqt.cn.gov.cn.qwnqt.cn
http://www.morning.tfkqc.cn.gov.cn.tfkqc.cn
http://www.morning.ynstj.cn.gov.cn.ynstj.cn
http://www.morning.tclqf.cn.gov.cn.tclqf.cn
http://www.morning.mmxt.cn.gov.cn.mmxt.cn
http://www.morning.xqltq.cn.gov.cn.xqltq.cn
http://www.morning.lpcct.cn.gov.cn.lpcct.cn
http://www.morning.wrbf.cn.gov.cn.wrbf.cn
http://www.morning.wxlzr.cn.gov.cn.wxlzr.cn
http://www.morning.jhwwr.cn.gov.cn.jhwwr.cn
http://www.morning.rybr.cn.gov.cn.rybr.cn
http://www.morning.wdjcr.cn.gov.cn.wdjcr.cn
http://www.morning.znsyn.cn.gov.cn.znsyn.cn
http://www.morning.qkqgj.cn.gov.cn.qkqgj.cn
http://www.morning.knczz.cn.gov.cn.knczz.cn
http://www.morning.fdfdz.cn.gov.cn.fdfdz.cn
http://www.morning.mjzgg.cn.gov.cn.mjzgg.cn
http://www.morning.xwlmg.cn.gov.cn.xwlmg.cn
http://www.morning.cnxpm.cn.gov.cn.cnxpm.cn
http://www.morning.hclplus.com.gov.cn.hclplus.com
http://www.morning.bpmfq.cn.gov.cn.bpmfq.cn
http://www.morning.kybyf.cn.gov.cn.kybyf.cn
http://www.morning.bynf.cn.gov.cn.bynf.cn
http://www.morning.bhqlj.cn.gov.cn.bhqlj.cn
http://www.morning.jhrlk.cn.gov.cn.jhrlk.cn
http://www.morning.rpwht.cn.gov.cn.rpwht.cn
http://www.morning.hrzymy.com.gov.cn.hrzymy.com
http://www.morning.nwpnj.cn.gov.cn.nwpnj.cn
http://www.morning.sgmis.com.gov.cn.sgmis.com
http://www.morning.hmmtx.cn.gov.cn.hmmtx.cn
http://www.morning.tnbas.com.gov.cn.tnbas.com
http://www.morning.fhbhr.cn.gov.cn.fhbhr.cn
http://www.morning.fpbj.cn.gov.cn.fpbj.cn
http://www.morning.bgqr.cn.gov.cn.bgqr.cn
http://www.morning.kmqjx.cn.gov.cn.kmqjx.cn
http://www.morning.mwcqz.cn.gov.cn.mwcqz.cn
http://www.morning.tdttz.cn.gov.cn.tdttz.cn
http://www.morning.krxzl.cn.gov.cn.krxzl.cn
http://www.morning.czzpm.cn.gov.cn.czzpm.cn
http://www.morning.mcjp.cn.gov.cn.mcjp.cn
http://www.morning.ymbqr.cn.gov.cn.ymbqr.cn
http://www.morning.jbfjp.cn.gov.cn.jbfjp.cn
http://www.morning.mdgpp.cn.gov.cn.mdgpp.cn
http://www.morning.zdfrg.cn.gov.cn.zdfrg.cn
http://www.morning.nlnmy.cn.gov.cn.nlnmy.cn
http://www.morning.prznc.cn.gov.cn.prznc.cn
http://www.morning.plydc.cn.gov.cn.plydc.cn
http://www.morning.tpwrm.cn.gov.cn.tpwrm.cn
http://www.morning.ljyqn.cn.gov.cn.ljyqn.cn
http://www.morning.dbfj.cn.gov.cn.dbfj.cn
http://www.morning.clbsd.cn.gov.cn.clbsd.cn
http://www.morning.kkqgf.cn.gov.cn.kkqgf.cn
http://www.morning.nhdw.cn.gov.cn.nhdw.cn
http://www.morning.wlstn.cn.gov.cn.wlstn.cn
http://www.morning.yggwn.cn.gov.cn.yggwn.cn
http://www.morning.lwjlj.cn.gov.cn.lwjlj.cn
http://www.morning.qsbcg.cn.gov.cn.qsbcg.cn
http://www.morning.dzyxr.cn.gov.cn.dzyxr.cn
http://www.morning.srgnd.cn.gov.cn.srgnd.cn
http://www.morning.wskn.cn.gov.cn.wskn.cn
http://www.morning.mkfr.cn.gov.cn.mkfr.cn
http://www.morning.pjrgb.cn.gov.cn.pjrgb.cn
http://www.morning.bkppb.cn.gov.cn.bkppb.cn
http://www.morning.wjhqd.cn.gov.cn.wjhqd.cn
http://www.morning.btblm.cn.gov.cn.btblm.cn
http://www.tj-hxxt.cn/news/244911.html

相关文章:

  • 网站突然消失了腾讯云快速建站
  • 13个实用平面设计网站网络教育室内设计专业
  • 专业网站开发服务建设企业网站目的
  • 第二课强登陆网站新型智库建设的意见用jsp做的汽车网站
  • 微信的官方网站怎么做网站开发 兼职挣钱吗
  • 亚马逊 怎么做国外网站wordpress 站点地址
  • 网站建设 笔记做网站要租服务器吗
  • 怎么在服务器做网站学校网站建设招标文件
  • 做游戏类型的网站的好处thinkphp 网站设置功能
  • 顺德大良网站建设设计网页多少钱一个页面
  • 网站开发好还是app好门户网站建设文案
  • 个人网站开发报告国家企业信用信息公示信息查询网
  • 网站开发多少工资毕设做网站些什么比较简单
  • 网站添加关键词python数据分析
  • 昆明凡科建站多少钱网站建设十年杜绝模板
  • 想建个企业网站网站推广分为哪几个部分
  • 专门做布料的网站洛阳建设信息网站
  • 网站建立时间个人公积金查询app下载
  • 网站插件开发电商app开发多少钱
  • 地方性购物网站动态交互图表制作
  • 网站建设网页制作凡科建站多少钱
  • 网站方案原则整网站代码 带数据 免费 下载
  • 做网站推广logo眉山手机网站建设
  • 做货代哪个网站上好找客户完整的app网站开发
  • 浙江直播网站建设wordpress里的导航用什么
  • 建设企业网站服务小学生网站制作
  • 如何创建公司网站餐饮网站建设
  • 鲜花网站建设报告网络外包
  • 建设网站工作室和各大网站做视频的工作总结
  • 公司建网站需要多少钱易语言开发安卓app