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

Linux网络编程 ——UDP 通信

Linux网络编程 ——UDP 通信

    • 1. UDP
      • 1.1 UDP 通信
      • 1.2 广播
      • 1.3 组播(多播)
    • 2. 本地套接字

1. UDP

1.1 UDP 通信

在这里插入图片描述

输入 man 2 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 : 通信的fd- buf : 要发送的数据- len : 发送数据的长度- flags : 0- dest_addr : 通信的另外一端的地址信息- addrlen : 前面地址的内存大小- 返回值:- 成功:字节个数- 失败:-1ssize_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 : 前面地址的内存大小- 返回值:- 成功:字节个数- 失败:-1

代码实现
(1)服务器端 udp_server.c

#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;
}

(2)客户端 udp_client.c

#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;
}
  • 运行结果:
    在这里插入图片描述

1.2 广播

    向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个 特殊的IP地址,这个IP中子网内 主机标志部分的二进制 全部1

  1. 只能局域网 中使用。
  2. 客户端 需要 绑定 服务器广播使用的 端口,才可以接收到广播消息。

在这里插入图片描述

  • 《Unix 网络编程》p151
    在这里插入图片描述
// 设置广播属性的函数
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的大小

代码实现
(1)服务器端 bro_server.c

#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.216.255", &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;
}

(2)客户端 bro_client.c

#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;int 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;
}
  • 运行结果:
    在这里插入图片描述
  • 产生丢包,客户端只能接收部分数据。

注意:

  • 同一个主机不能使用相同的端口号,一个端口号只能标识一个进程!
  • 测试的话可以克隆一个主机,设置其 ip 在同一个网段(局域网)。

1.3 组播(多播)

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

  1. 组播 既可以用于局域网,也可以用于广域网
  2. 客户端 需要加入 多播组,才能接收到多播的数据

在这里插入图片描述
组播地址

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

在这里插入图片描述

  • 设置组播
    • 《Unix 网络编程》p151
      在这里插入图片描述
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);// 服务器设置多播的信息,外出接口- level : IPPROTO_IP- optname : IP_MULTICAST_IF- optval : struct in_addr// 客户端加入到多播组:- level : IPPROTO_IP- optname : IP_ADD_MEMBERSHIP- optval : struct ip_mreqstruct ip_mreq
{/* IP multicast address of group. */struct in_addr imr_multiaddr; // 组播的IP地址/* Local IP address of interface. */struct in_addr imr_interface; // 本地的IP地址
};typedef uint32_t in_addr_t;struct in_addr
{in_addr_t s_addr;
};

代码实现
(1)服务器端 multi_server.c

#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;
}

(2)客户端 multi_client.c

#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;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;
}
  • 运行结果:
    在这里插入图片描述

2. 本地套接字

本地套接字的作用:本地的 进程间通信

  • 有关系的进程间的通信
  • 没有关系的进程间的通信

本地套接字 实现流程和 网络套接字 类似,一般呢采用TCP的通信流程
在这里插入图片描述

// 本地套接字通信的流程 - tcp// 服务器端
1. 创建监听的套接字int lfd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0);
2. 监听的套接字绑定本地的套接字文件 -> server端struct sockaddr_un addr;// 绑定成功之后,指定的sun_path中的套接字文件会自动生成。bind(lfd, addr, len);
3. 监听listen(lfd, 100);
4. 等待并接受连接请求struct sockaddr_un cliaddr;int cfd = accept(lfd, &cliaddr, len);
5. 通信接收数据:read/recv发送数据:write/send
6. 关闭连接close();// 客户端的流程
1. 创建通信的套接字int fd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0);
2. 监听的套接字绑定本地的IP 端口struct sockaddr_un addr;// 绑定成功之后,指定的sun_path中的套接字文件会自动生成。bind(lfd, addr, len);
3. 连接服务器struct sockaddr_un serveraddr;connect(fd, &serveraddr, sizeof(serveraddr));
4. 通信接收数据:read/recv发送数据:write/send
5. 关闭连接close();
// 头文件: sys/un.h
#define UNIX_PATH_MAX 108struct sockaddr_un {sa_family_t sun_family; // 地址族协议 af_localchar sun_path[UNIX_PATH_MAX]; // 套接字文件的路径, 这是一个伪文件, 大小永远=0
};

代码实现
(1)服务器端 ipc_server.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/un.h>int main() {unlink("server.sock");// 1.创建监听的套接字int lfd = socket(AF_LOCAL, SOCK_STREAM, 0);if(lfd == -1) {perror("socket");exit(-1);}// 2.绑定本地套接字文件struct sockaddr_un addr;addr.sun_family = AF_LOCAL;strcpy(addr.sun_path, "server.sock");int ret = bind(lfd, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1) {perror("bind");exit(-1);}// 3.监听ret = listen(lfd, 100);if(ret == -1) {perror("listen");exit(-1);}// 4.等待客户端连接struct sockaddr_un cliaddr;int len = sizeof(cliaddr);int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);if(cfd == -1) {perror("accept");exit(-1);}printf("client socket filename: %s\n", cliaddr.sun_path);// 5.通信while(1) {char buf[128];int len = recv(cfd, buf, sizeof(buf), 0);if(len == -1) {perror("recv");exit(-1);} else if(len == 0) {printf("client closed....\n");break;} else if(len > 0) {printf("client say : %s\n", buf);send(cfd, buf, len, 0);}}close(cfd);close(lfd);return 0;
}

(2)客户端 ipc_client.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/un.h>int main() {unlink("client.sock");// 1.创建套接字int cfd = socket(AF_LOCAL, SOCK_STREAM, 0);if(cfd == -1) {perror("socket");exit(-1);}// 2.绑定本地套接字文件struct sockaddr_un addr;addr.sun_family = AF_LOCAL;strcpy(addr.sun_path, "client.sock");int ret = bind(cfd, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1) {perror("bind");exit(-1);}// 3.连接服务器struct sockaddr_un seraddr;seraddr.sun_family = AF_LOCAL;strcpy(seraddr.sun_path, "server.sock");ret = connect(cfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret == -1) {perror("connect");exit(-1);}// 4.通信int num = 0;while(1) {// 发送数据char buf[128];sprintf(buf, "hello, i am client %d\n", num++);send(cfd, buf, strlen(buf) + 1, 0);printf("client say : %s\n", buf);// 接收数据int len = recv(cfd, buf, sizeof(buf), 0);if(len == -1) {perror("recv");exit(-1);} else if(len == 0) {printf("server closed....\n");break;} else if(len > 0) {printf("server say : %s\n", buf);}sleep(1);}close(cfd);return 0;
}

注:仅供学习参考,如有不足,欢迎指正!

相关文章:

  • Google发布Genie硬杠Sora:通过大量无监督视频训练最终生成可交互虚拟世界
  • SpringBoot中实现拦截器和过滤器
  • 计算机视觉 了解OpenCV、COLMAP、PyTorch3D 和 OpenGL 中坐标系3D转换的简要指南
  • 如何搭建Nacos集群
  • 刷题DAY9 | LeetCode 28-实现 strStr() 459-重复的子字符串
  • Golang 程序启动原理详解
  • shadertoy 游戏《来自星尘》摇杆复刻
  • tsc : 无法加载文件 C:\Users\Administrat\AppData\Roaming\npm\tsc.ps 1,因为在此系统上禁止运行脚本
  • vmware安装图形版ubuntu(20.4)
  • 【Golang星辰图】探索网络和HTTP的奇妙世界:使用Go语言打造高性能应用
  • 华为配置WLAN高密业务示例
  • 【数据结构】复杂度详解
  • 这里推荐一款unity3d人物动物控制器详细的等学会再写文章
  • 08 OpenCV 腐蚀和膨胀
  • Aws Ec2服务器设置密码登录
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 分享一款快速APP功能测试工具
  • 30天自制操作系统-2
  • angular学习第一篇-----环境搭建
  • bootstrap创建登录注册页面
  • Javascript Math对象和Date对象常用方法详解
  • Java知识点总结(JavaIO-打印流)
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Spring Boot MyBatis配置多种数据库
  • 给Prometheus造假数据的方法
  • 全栈开发——Linux
  • 世界上最简单的无等待算法(getAndIncrement)
  • 小程序button引导用户授权
  • 一文看透浏览器架构
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • ​人工智能书单(数学基础篇)
  • # 飞书APP集成平台-数字化落地
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (差分)胡桃爱原石
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (推荐)叮当——中文语音对话机器人
  • .net 8 发布了,试下微软最近强推的MAUI
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .NET Core中的去虚
  • .NET 反射 Reflect
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .net开发时的诡异问题,button的onclick事件无效
  • .NET序列化 serializable,反序列化
  • .net中我喜欢的两种验证码
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [BZOJ 3282] Tree 【LCT】
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [bzoj1038][ZJOI2008]瞭望塔
  • [C#小技巧]如何捕捉上升沿和下降沿
  • [hihocoder1395] 最大权闭合子图