Linux网络编程(四)——UDP通信

news/2025/2/21 5:52:42/

目录

0x01 UDP协议

一、UDP通信简介以及接口

二、UDP的接口

三、UDP收发例程

0x02 广播

一、设置广播数据函数接口

二、广播代码实现

0x03 组播(多播)

一、组播地址

二、设置组播函数接口

三、代码实现


0x01 UDP协议

一、UDP通信简介以及接口

UDP是一个面向无连接的,不可靠的服务,功能即为在IP的数据报服务之上增加了最基本的服务:复用和分用以及差错检测。

  • UDP通信不需要建立连接,因此不需要进行connect()操作。

  • UDP通信过程中,每次都需要指定数据接收端的IP和端口。

  • UDP不对收到的数据进行排序,在UDP报文的首部中并没有关于数据顺序的信息。

  • UDP对接收到的数据报不回复确认信息,发送端不知道数据是否被正确接收,也不会重发数据。

  • 如果发生了数据丢失,不存在丢一半的情况,如果丢当前这个数据报就全部丢失了。

  • UDP没有拥塞控制,应用层能够更好的控制要发送的数据和发送时间,网络中的拥塞控制也不会影响主机的发送速率,能容忍一些数据的丢失,但是不能允许有较大的时延。

二、UDP的接口

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);- 参数:- sockfd : 通信的fd- buf : 要发送的数据- len : 发送数据的长度- flags : 0- dest_addr : 通信的另外一端的地址信息- addrlen : 地址的内存大小- 返回:-n :发送字节的数量 -0 :失败
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);- 参数:- sockfd : 通信的fd- buf : 接收数据的数组- len : 数组的大小- flags : 0- src_addr : 用来保存另外一端的地址信息,不需要可以指定为NULL- addrlen : 地址的内存大小- 返回:- n:成功返回字节个数- 0:失败

三、UDP收发例程

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   // 服务器的地址信息struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);int num = 0;// 3.通信while(1) {// 发送数据char sendBuf[128];sprintf(sendBuf, "hello , i am client %d \n", num++);sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&saddr, sizeof(saddr));// 接收数据int num = recvfrom(fd, sendBuf, sizeof(sendBuf), 0, NULL, NULL);printf("server say : %s\n", sendBuf);sleep(1);}close(fd);return 0;
}

  服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9999);addr.sin_addr.s_addr = INADDR_ANY;// 2.绑定int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1) {perror("bind");exit(-1);}// 3.通信while(1) {char recvbuf[128];char ipbuf[16];struct sockaddr_in cliaddr;int len = sizeof(cliaddr);// 接收数据int num = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&cliaddr, &len);printf("client IP : %s, Port : %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),ntohs(cliaddr.sin_port));printf("client say : %s\n", recvbuf);// 发送数据sendto(fd, recvbuf, strlen(recvbuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));}close(fd);return 0;
}

0x02 广播

在局域网中可以使用单播的形式,即一对一通讯,也可以使用广播的形式,即向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1(即主机IP为255)。

例如192.168.10.20,其中20为主机IP,其他的为网络IP。若主机IP为0,则代表这是个子网IP;若为255,则代表为广播IP。

  • 只能在局域网中使用

  • 客户端需要绑定服务器广播使用的端口,才可以接收到广播消息,也就是说客户端需要绑定IP为192.168.10.255,并且需要绑定与广播的服务器的端口。

一、设置广播数据函数接口

// 设置广播属性的函数
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);- sockfd : 文件描述符- level : SOL_SOCKET- optname : SO_BROADCAST- optval : int类型的值,为1表示允许广播- optlen : optval的大小	

二、广播代码实现

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   // 2.设置广播属性int op = 1;setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof(op));// 3.创建一个广播的地址struct sockaddr_in cliaddr;cliaddr.sin_family = AF_INET;cliaddr.sin_port = htons(9999);inet_pton(AF_INET, "192.168.193.255", &cliaddr.sin_addr.s_addr);	//本机的广播地址使用命令ifconfig查看// 3.通信int num = 0;while(1) {char sendBuf[128];sprintf(sendBuf, "hello, client....%d\n", num++);// 发送数据sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));printf("广播的数据:%s\n", sendBuf);sleep(1);}close(fd);return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   struct in_addr in;// 2.客户端绑定本地的IP和端口struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9999);addr.sin_addr.s_addr = INADDR_ANY;		//绑定本机IPint ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1) {perror("bind");exit(-1);}// 3.通信while(1) {char buf[128];// 接收数据int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);printf("server say : %s\n", buf);}close(fd);return 0;
}

0x03 组播(多播)

单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组 IP 接口。单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。

  • 组播既可以用于局域网,也可以用于广域网。

  • 客户端需要加入多播组,才能接收到多播的数据。

一、组播地址

IP多播通信必须依赖于IP多播地址,在IPv4中它的范围从224.0.0.0239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:

IP地址说明
224.0.0.0~224.0.0.255局部链接多播地址:是为路由协议和其它用途保留的地址,路由器并不转发属于此范围的IP包
224.0.1.0~224.0.1.255预留多播地址:公用组播地址,可用于Internet;使用前需要申请
224.0.2.0~238.255.255.255预留多播地址:用户可用组播地址(临时组地址),全网范围内有效
239.0.0.0~239.255.255.255本地管理组播地址,可供组织内部使用,类似于私有 IP 地址,不能用于 Internet,可限制多播范围

二、设置组播函数接口

int setsockopt(int sockfd, int level, int optname,const void *optval,socklen_t optlen);// 服务器设置多播的信息,外出接口- level : IPPROTO_IP- optname : IP_MULTICAST_IF- optval : struct in_addrtypedef uint32_t in_addr_t;struct in_addr{in_addr_t s_addr;};// 客户端加入到多播组:- level : IPPROTO_IP- optname : IP_ADD_MEMBERSHIP- optval : struct ip_mreq	struct ip_mreq{/* IP multicast address of group.*/struct in_addr imr_multiaddr;  // 组播的IP地址/* Local IP address of interface.*/struct in_addr imr_interface;	// 本地的IP地址};

以上的参数设置主要参照于《UNIX网络编程》。

三、代码实现

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   // 2.设置多播的属性,设置外出接口struct in_addr imr_multiaddr;// 初始化多播地址inet_pton(AF_INET, "239.0.0.10", &imr_multiaddr.s_addr);setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof(imr_multiaddr));// 3.初始化客户端的地址信息,在多播中需要表明客户端的信息struct sockaddr_in cliaddr;cliaddr.sin_family = AF_INET;cliaddr.sin_port = htons(9999);inet_pton(AF_INET, "239.0.0.10", &cliaddr.sin_addr.s_addr);// 3.通信int num = 0;while(1) {char sendBuf[128];sprintf(sendBuf, "hello, client....%d\n", num++);// 发送数据sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));printf("组播的数据:%s\n", sendBuf);sleep(1);}close(fd);return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>int main() {// 1.创建一个通信的socket,使用UDP通信协议int fd = socket(PF_INET, SOCK_DGRAM, 0);if(fd == -1) {perror("socket");exit(-1);}   struct in_addr in;// 2.客户端绑定本地的IP和端口struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9999);addr.sin_addr.s_addr = INADDR_ANY;int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1) {perror("bind");exit(-1);}struct ip_mreq op;inet_pton(AF_INET, "239.0.0.10", &op.imr_multiaddr.s_addr);op.imr_interface.s_addr = INADDR_ANY;// 加入到多播组setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof(op));// 3.通信while(1) {char buf[128];// 接收数据int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);printf("server say : %s\n", buf);}close(fd);return 0;
}

需要注意的是,多播与广播只能UDP,对于TCP只能一对一。


http://www.ppmy.cn/news/42552.html

相关文章

Web自动化测试-【Selenium环境部署Edge】

Selenium Web自动化测试工具 之前写过一篇关于自动化测试的博客&#xff0c;里面是有的chrome驱动&#xff0c;由于不适配缘故&#xff0c;更新以下Edge驱动。 自动化测试 Selenium环境部署 准备 Edge 浏览器准备 Edge 驱动包 a .查看自己的Edge浏览器版本&#xff08;浏览器版…

232:vue+openlayers选择左右两部分的地图,不重复,横向卷帘

第232个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中自定义js实现横向卷帘。这个示例中从左右两个选择框中来选择不同的地图,做了不重复的处理,即同一个数组,两部分根据选择后的状态做disabled处理,避免重复选择。 直接复制下面的 vue+openlayers…

【模型复现】densenet,增加残差结构连接,复用特征图的角度降低了计算量还提升了精度,transition_block压缩特征图

相比ResNet&#xff0c;DenseNet[1608.06993] Densely Connected Convolutional Networks (arxiv.org)提出了一个更激进的密集连接机制&#xff1a;即互相连接所有的层&#xff0c;具体来说就是每个层都会接受其前面所有层作为其额外的输入。下图为DenseNet的密集连接机制。可以…

Vue学习——【第五弹】

前言 上一篇文章 Vue学习——【第四弹】 中学到了数据代理&#xff0c;这篇文章接着学习 Vue中的事件处理。 事件处理 我们在学习JavaScript时就经常接触事件处理&#xff0c;比如在进行表单、按钮、列表折叠等操作时&#xff0c;我们就经常用到 click&#xff08;点击&…

【SSM】Spring6(十一.Spring对事务支持)

文章目录1.引入事务场景1.1准备数据库1.2 创建包结构1.3 创建POJO类1.4 编写持久层1.5 编写业务层1.6 Spring配置文件1.7 表示层&#xff08;测试&#xff09;1.8 模拟异常2.Spring对事务的支持2.1 spring事务管理API2.2 spring事务之注解方式2.3 事务的属性2.4 事务的传播行为…

Linux -- 基础IO

文章目录1. 基础认识2. 回顾C文件接口2.1 现象一2.2 现象二2.3 fprintf()函数回顾2.4 fnprintf()函数使用2.5 "a"模式3. 系统接口3.1 open()和close()3.2 write()3.3 read()3.4 C文件接口和系统接口关系3.5 文件描述符3.6 深度理解Linux下一切皆文件3.7 FILE是什么3.…

SwiftUI_属性装饰器

关键词ObservableObject / PublishedEnvironmentObjectStateBindingEnvironmentObservableObject / Published ObservedObject 的用处和 State 非常相似&#xff0c;从名字看来它是来修饰一个对象的&#xff0c;这个对象可以给多个独立的 View 使用。如果你用 ObservedObject …

Centos 服务器放行TCP、UDP端口教程

Centos 服务器放行TCP、UDP端口教程1、telnet2、nc3、firewall1&#xff09;放行TCP端口2&#xff09;放行UDP端口3&#xff09;放行端口范围8888-99994&#xff09;关闭某个端口5&#xff09;查看已经放行的端口6&#xff09;查看防火墙状态7&#xff09;开启防火墙8&#xff…