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

《LINUX系统编程》笔记p13

UDP通信

UDP (User Datagram Protocol)

特点:无连接、不可靠、数据报协议。

用途:用于不需要可靠连接,但实时性要求比较强的场合,如音视频数据的网络播放。

UDP协议头

创建套接字

int socket(int domain, int type, int protocol);
// 参数
//    domain是网络层协议
//       AF_NET:IPv4
//       AF_NET6:IPv6
//    type: 传输层协议类型
//       SOCK_STREAM  TCP协议(数据流协议)
//       SOCK_DGRAM   UDP协议(数据报文协议)
//    protocal: 子协议,用于其他协议,一般为0.
// 返回值:套接字的文件描述符

发送数据函数 sendto

#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 UDP套接字
//    buf 发送缓冲区。
//    len 发送的字节数(不能为零)。
//    flags: 标记位,默认为零。
//    dest_addr: 目标地址的 IPv4 地址族结构。
//    addrlen: 目标地址的 IPv4 地址族结构的长度。
// 返回值:返回发送的字节数,错误返回-1同时设置错误号。

接收数据的函数recvfrom

#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
// 参数
//    sockfd UDP套接字
//    buf 接收缓冲区。
//    len 接收缓冲区的长度。
//    flags: 标记位,默认为零。
//    dest_addr: 发送方地址的 IPv4 地址族结构。
//    addrlen: 发送方地址的 IPv4 地址族结构的长度的地址。
// 返回值:返回发送的字节数,错误返回-1同时设置错误号。

关闭套接字

close(sock_fd);

udp客户端代码示例

// UDP 客户端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define SERVER_IP "192.144.206.112"
#define SERVER_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区char buf_write[BUF_SIZE];  // 写数据缓冲区// 创建套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");}sin.sin_family = AF_INET;sin.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &sin.sin_addr);// 与服务器端通信while(1) {ret = read(0, buf_write, BUF_SIZE);buf_write[ret-1] = 0;// 将buf_write的内容发送给,服务器端.ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {perror("sendto");break;}if(0 == strcmp("exit", buf_write))break;ret = recv(sock_fd, buf_read, BUF_SIZE-1, 0);buf_read[ret] = 0;printf("服务器回复:%s\n", buf_read);}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

udp服务器端代码

// UDP 服务器端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define SERVER_IP "192.144.206.112"
#define SERVER_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区char buf_write[BUF_SIZE];  // 写数据缓冲区// 创建套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");}sin.sin_family = AF_INET;sin.sin_port = htons(SERVER_PORT);sin.sin_addr.s_addr = INADDR_ANY;// inet_pton(AF_INET, SERVER_IP, &sin.sin_addr);ret = bind(sock_fd, (struct sockaddr*)&sin,sizeof(sin));if (-1 == ret) {perror("bind");close(sock_fd);return 1;}// 与服务器端通信while(1) {socklen_t cin_len = sizeof(cin);// cin 用来保存客户端地址ret = recvfrom(sock_fd, buf_read, BUF_SIZE-1, 0,(struct sockaddr*)&cin, &cin_len);// 打印客户端的IP地址和端口号printf("客户端IP:%s, 端口号:%d\n",inet_ntop(AF_INET, &cin.sin_addr, buf_write,BUF_SIZE), ntohs(cin.sin_port));buf_read[ret] = 0;if (0 == strcmp("hello", buf_read)) {strcpy(buf_write, "Hi");} else  {strcpy(buf_write,"I don't know what you said!");}ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&cin, sizeof(cin));if (-1 == ret) {perror("sendto");break;}}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

UDP链接图示

广播

局域网内一对多的UDP传输方式。

广播地址:

255.255.255.255 所有的网络设备都发送
IP地址 | ~子网掩码
如: 192.168.1.100 | ~255.255.255.0.的广播地址是
192.168.1.255

设置和获取套接字选项

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
// 参数
//    sockfd 套接字
//    level 套接字层:如 SOL_SOCKET、IPPROTO_IP
//    optname 选项名:
//         SO_REUSEADDR  让端口号可以重复绑定
//         IP_ADD_MEMBERSHIP 加入多播组
//         IP_DROP_MEMBERSHIP  退出多播组
//    optval: 选项值。
//    optlen: 选项值的内存长度。
// 返回值:返回发送的字节数,错误返回-1同时设置错误号。

广播接收端示例代码

// UDP 广播接收端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define BROADCAST_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区// char buf_write[BUF_SIZE];  // 写数据缓冲区int reuse_enble = 1;  // 值为 1 表示同意int cin_len = sizeof(cin);  // cin的长度// 创建UDP套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");return 1;}// 设置套接字选项,允许多个进程绑定到同一个端口ret = setsockopt(sock_fd,  SOL_SOCKET, SO_REUSEADDR, &reuse_enble, sizeof(reuse_enble));if (-1 == ret) {perror("setsockopt");return 2;}//绑定本局域网内的某个广播用的端口号.sin.sin_family = AF_INET;sin.sin_port = htons(BROADCAST_PORT);sin.sin_addr.s_addr = INADDR_ANY;ret = bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin));if (-1 == ret) {perror("bind");return 3;}printf("准备接收广播...\n");// 与服务器端通信while(1) {ret = recvfrom(sock_fd, buf_read, BUF_SIZE-1, 0, (struct sockaddr*)&cin, &cin_len);buf_read[ret] = 0;printf("收到广播:%s\n", buf_read);if (0 == strcmp("exit", buf_read))break;char buf[20];printf("发送者IP:%s, 端口号:%d\n",inet_ntop(AF_INET, &cin.sin_addr, buf,20),ntohs(cin.sin_port));}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

UDP 广播发送端实现

// UDP 广播发送端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define BROADCAST_IP "255.255.255.255"
// #define BROADCAST_IP "10.11.9.255"
// #define BROADCAST_IP "127.0.0.255"
#define BROADCAST_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构char buf_write[BUF_SIZE];  // 写数据缓冲区int broadcast_enble = 1;  // 值为 1 表示同意// 创建UDP套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");return 1;}// 设置套接字选项,允许广播ret = setsockopt(sock_fd,  SOL_SOCKET, SO_BROADCAST, &broadcast_enble, sizeof(broadcast_enble));if (-1 == ret) {perror("setsockopt");return 2;}//设置广播用的IP和端口号信息sin.sin_family = AF_INET;sin.sin_port = htons(BROADCAST_PORT);// sin.sin_addr.s_addr = INADDR_ANY;inet_pton(AF_INET, BROADCAST_IP, &sin.sin_addr);printf("准备发送广播...\n");// 循环广播 60 秒后退出for (int i = 0; i < 60; i++) {sprintf(buf_write, "广播数字:%d", i);ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {close(sock_fd);return 1;}sleep(1);}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

多播

什么是多播
多播,也叫组播,是一种网络数据包传输方式,它允许一个发送者向一组特定的接收者发送数据包。

单播、广播、多播

单播
方式: 一对一通信。发送者为每一个接收者都发送一份独立的数据副本。
比喻: 打电话。如果你想通知10个人,你需要打10个电话,把同样的话说10遍。
缺点: 当接收者数量巨大时(如视频直播有百万观众),发送者和网络链路的压力会呈指数级增长,效率极低,浪费带宽。

广播
方式: 一对所有通信。发送者把数据发到网络中的所有设备,无论它们是否需要。
比喻: 大喇叭广播。
缺点: 安全性差(所有人都能收到),会干扰不需要该数据的设备,而且路由器默认会隔离广播域,意味着广播包通常无法跨越网段(如从公司财务部网络传到研发部网络),因此不能用于互联网范围的传输。

多播

方式: 一对一组通信。发送者只发送一份数据副本。网络中的路由器(如果配置了多播路由)会负责根据需要将数据复制并转发给有接收者的分支路径。
比喻: 杂志资料,给每个省,每个省自己复制在下发给需要的县、县在复制给需要的人。
优点: 极大节省了发送者和网络带宽。无论组内有多少接收者,发送者都只发送一份数据。只有在网络路径需要分叉时,路由器才会复制数据包。

多播组地址:这是一个逻辑上的组,用一个IP地址来标识。

IPv4中,多播地址合法的范围是 224.0.0.0 到 239.255.255.255。

一般 239.0.0.0 ~ 239.255.255.255 常用于开发测试(相当于局域网的多播地址),测试时不会和公网数据冲突。

UDP 多播接收端实现

// UDP 多播接收端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define MULTICAST_GROUP "239.0.0.10" // 标准多播地址
#define MULTICAST_PORT 6666
#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  //IPv4地址族结构struct sockaddr_in cin;  //IPv4地址族结构int cin_len = sizeof(cin);int reuse_enble = 1;struct ip_mreq mreq;  // 多播放组请求结构char buf_read[BUF_SIZE];  // 读数据缓冲区// 创建UDP套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");return 1;}// 设置套接字选项,允许多个进程绑定到同一个端口ret = setsockopt(sock_fd,  SOL_SOCKET, SO_REUSEADDR, &reuse_enble, sizeof(reuse_enble));if (-1 == ret) {perror("setsockopt");return 2;}//绑定本局域网内的某个多播用的端口号.sin.sin_family = AF_INET;sin.sin_port = htons(MULTICAST_PORT);sin.sin_addr.s_addr = INADDR_ANY;ret = bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin));if (-1 == ret) {perror("bind");return 3;}// 加入多播(组播)inet_pton(AF_INET, MULTICAST_GROUP, &mreq.imr_multiaddr.s_addr);  // 设置多播组的地址mreq.imr_interface.s_addr = INADDR_ANY;  // 设置本机器的地址。ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));if (-1 == ret) {perror("setsockopt"); // 加入多播组失败.return 4;}printf("准备接收多播(组播)...\n");// 与服务器端通信while(1) {ret = recvfrom(sock_fd, buf_read, BUF_SIZE-1, 0, (struct sockaddr*)&cin, &cin_len);buf_read[ret] = 0;printf("收到广播:%s\n", buf_read);if (0 == strcmp("exit", buf_read))break;char buf[20];printf("发送者IP:%s, 端口号:%d\n",inet_ntop(AF_INET, &cin.sin_addr, buf,20),ntohs(cin.sin_port));}// 退出多播(组播)inet_pton(AF_INET, MULTICAST_GROUP, &mreq.imr_multiaddr.s_addr);  // 设置多播组的地址mreq.imr_interface.s_addr = INADDR_ANY;  // 设置本机器的地址。ret = setsockopt(sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));if (-1 == ret) {perror("setsockopt"); // 加入多播组失败.return 4;}// 关闭套接字 close(sock_fd);printf("主进程结束\n");return 0;
}

UDP 多播发送端实现

// UDP 多播发送端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define MULTICAST_GROUP "239.0.0.10"
#define BROADCAST_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构char buf_write[BUF_SIZE];  // 写数据缓冲区int reuse_enble = 1;  // 值为 1 表示同意int ttl = 64; // 生存时间,数据包可以经过64跳。// 创建UDP套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");return 1;}// 2. 设置多播组TTL(生存时间)ret = setsockopt(sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));if (-1 == ret) {perror("setsockopt");return 2;}// 3. 让端口号可以重新绑定ret = setsockopt(sock_fd,  SOL_SOCKET, SO_REUSEADDR, &reuse_enble, sizeof(reuse_enble));if (-1 == ret) {perror("setsockopt");return 2;}//4. 设置多播用的IP和端口号信息sin.sin_family = AF_INET;sin.sin_port = htons(BROADCAST_PORT);// sin.sin_addr.s_addr = INADDR_ANY;inet_pton(AF_INET, MULTICAST_GROUP, &sin.sin_addr);printf("准备发送广播...\n");// 循环多播 60 秒后退出for (int i = 0; i < 60; i++) {sprintf(buf_write, "广播数字:%d", i);ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {close(sock_fd);return 1;}sleep(1);}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

文章转载自:

http://pTWQpZPJ.hkchp.cn
http://DzRy3iGv.hkchp.cn
http://LRc7I1VN.hkchp.cn
http://hEzxINuG.hkchp.cn
http://90wuZjEM.hkchp.cn
http://qrNrHtY8.hkchp.cn
http://FJQ4flx7.hkchp.cn
http://kB6xRq9P.hkchp.cn
http://VxUIiF7E.hkchp.cn
http://iW7Uw59Z.hkchp.cn
http://wisVApze.hkchp.cn
http://89edif8a.hkchp.cn
http://GNs2mdN3.hkchp.cn
http://LVtZPo4R.hkchp.cn
http://Lu4vw6ff.hkchp.cn
http://eQVRgQ3b.hkchp.cn
http://mesQCo4P.hkchp.cn
http://2rka6iQ4.hkchp.cn
http://ZsctVIx0.hkchp.cn
http://WybNhuRE.hkchp.cn
http://nAO9WUIt.hkchp.cn
http://t78ysXFp.hkchp.cn
http://ewqnBuHC.hkchp.cn
http://hz0Ojx6Q.hkchp.cn
http://HRVUQni8.hkchp.cn
http://aOA2XYNr.hkchp.cn
http://qmqZGm93.hkchp.cn
http://kjhkkITs.hkchp.cn
http://tDUbmj2y.hkchp.cn
http://NH2mkndh.hkchp.cn
http://www.dtcms.com/a/387148.html

相关文章:

  • Spring Cloud-面试知识点(组件、注册中心)
  • 2.2 定点数的运算 (答案见原书 P93)
  • 使用数据断点调试唤醒任务时__state的变化
  • 力扣周赛困难-3681. 子序列最大 XOR 值 (线性基)
  • Spring IOC 与 Spring AOP
  • 【FreeRTOS】队列API全家桶
  • 【Docker项目实战】使用Docker部署Cup容器镜像更新工具
  • (笔记)内存文件映射mmap
  • springboot传输文件,下载文件
  • 基于51单片机的出租车计价器霍尔测速设计
  • 【笔记】Agent应用开发与落地全景
  • C++ STL底层原理系列学习路线规划
  • LAN口和WAN口
  • Dify + Bright Data MCP:从实时影音数据到可落地的智能体生产线
  • 数据库--使用DQL命令查询数据(二)
  • 【FreeRTOS】创建一个任务的详细流程
  • CKA06--storageclass
  • 宝塔安装以及无法打开时的CA证书配置全攻略
  • wend看源码-Open_Deep_Research(LangChain)
  • 摄像头文档识别与透视变化技术和背景建模技术(追踪)
  • 123、【OS】【Nuttx】【周边】效果呈现方案解析:find 格式化打印
  • DC-4靶机渗透
  • 大模型在线对话平台集锦(持续更新ing...)
  • JavaScript中 i++ 与 ++i
  • 【cookie】JavaScript操作增删改查
  • OC-AFNetworking
  • Java全栈学习笔记35
  • kylin v10 系统 上 qt 5.15.17版本构建及使用
  • Linux:基于环形队列的生产者消费模型
  • Nginx 配置 Vue 项目 Hash/History 模式路由跳转错误的解决方案