Linux——网络编程——UDP

网络编程之 UDP  用户数据报

1、特性: 无链接  不可靠  大数据   

2、框架: C/S模式 

   server:socket() ===>bind()===>recvfrom()===>close()
   client:socket() ===>bind()===>sendto() ===>close()


注意:socket()的参数需要调整。

      socket(PF_INET,SOCK_DGRAM,0);

      bind() 客户端是可选的,服务器端是比选的。

1、数据有边界,

2、发送数据和接受数据的次数使一致的,

同时长度要匹配,不然会产生数据丢弃的情况。

3、recvfrom会阻塞

4、  没有recvfrim 不会导致sendto阻塞 

一、发送接收函数:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
             const struct sockaddr *dest_addr, socklen_t addrlen);

功能:用于UDP协议中向对方发送数据。
参数:sockfd  本地的套接字id
      buff    本地的数据存储,一般是要发送的数据。
      len     要发送的数据长度
      flags   要发送数据方式,0 表示阻塞发送。

      dest_addr: 必选,表示要发送到的目标主机信息结构体。
      addrlen :目标地址长度。

返回值:成功  发送的数据长度
        失败   -1;


二ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                  struct sockaddr *src_addr, socklen_t *addrlen);

功能:用于UDP协议中获取对方发送的数据。
参数:sockfd 本地的套接字id
      buff   要存储数据的内存区,一般是数组或者动态内存。
      len    要获取的数据长度,一般是buff的大小。
      flags  获取方式,0 阻塞

      src_addr 可选,表示对方的地址信息结构体,
                  如果为NULL,表示不关心对方地址。
      addrlen  对方地址信息结构体大小。
                  如果对方地址是NULL,则该值也为NULL。
返回值:成功 接收到的数据长度
        失败  -1;

sever.c

#include <stdio.h>     // 标准输入输出库  
#include <stdlib.h>    // 标准库,包含exit等函数  
#include <unistd.h>    // UNIX标准函数定义,如sleep(但这里未使用)  
#include <string.h>    // 字符串操作函数,如bzero(注意:bzero已被弃用,建议使用memset)  
#include <sys/types.h> // 基本数据类型定义  
#include <sys/socket.h> // 套接字编程接口  
#include <netinet/in.h> // 定义IPv4地址  
#include <netinet/ip.h> // 虽然包含了,但在本程序中未直接使用到IP协议头  
#include <arpa/inet.h>  // 定义IP地址转换函数  
#include <time.h>       // 包含时间处理函数  typedef struct sockaddr * (SA);  int main(int argc, char *argv[])  
{  // 创建一个UDP套接字  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);  if (-1 == sockfd) {  // 如果创建套接字失败,则打印错误信息并退出  perror("socket");  exit(1);  }  // 定义服务器和客户端地址结构体变量,并清零  struct sockaddr_in ser, cli;  bzero(&ser, sizeof(ser)); // 使用bzero函数清零结构体,注意bzero函数已被废弃,建议使用memset  bzero(&cli, sizeof(cli)); // 同样,建议使用memset  // 设置服务器地址信息  ser.sin_family = AF_INET;  ser.sin_port = htons(50000); // 设置服务器端口号,进行网络字节序转换  ser.sin_addr.s_addr = inet_addr("192.168.203.128"); // 设置服务器IP地址  // 将套接字绑定到服务器地址和端口上  // 注意:这通常是在服务器程序中完成的,而不是客户端  int ret = bind(sockfd, (SA)&ser, sizeof(ser));  if (-1 == ret) {  perror("bind");  exit(1);  }  // 初始化客户端地址长度变量  socklen_t len = sizeof(cli);  // 进入无限循环,接收数据并发送响应  while (1) {  char buf[512] = {0}; // 定义一个接收缓冲区,并初始化为空  // 接收数据,注意这里也更新了cli的地址和长度信息  recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);  // 获取当前时间  time_t tm;  time(&tm);  // 将接收到的数据和当前时间追加到buf中(注意:这可能会导致缓冲区溢出)  // 更安全的做法是先检查buf中剩余的空间  sprintf(buf, "%s %s", buf, ctime(&tm));  // 发送响应给客户端  // 注意:这里使用了之前从recvfrom获取的cli地址和长度信息  sendto(sockfd, buf, strlen(buf), 0, (SA)&cli, len);  // 但注意:strlen(buf)可能不包含ctime返回的字符串的结束符'\n'  // 更安全的做法是使用snprintf来确保不会超出buf的大小  // 这里没有添加延时,程序会尽可能快地处理每一个接收到的数据包  }  // 注意:由于存在无限循环,这里的close和return语句实际上不会被执行  // 但为了代码的完整性,我还是保留了它们  // 如果需要退出程序,应该在循环内部添加适当的退出条件  // 关闭套接字(这行代码实际上不会被执行)  close(sockfd);  return 0; // 程序正常结束(这行代码实际上也不会被执行)  
}  

 client.c

#include <stdio.h>     // 标准输入输出库  
#include <stdlib.h>    // 标准库,包含exit等函数  
#include <unistd.h>    // UNIX标准函数定义,如sleep  
#include <string.h>    // 字符串操作函数,如strlen, bzero  
#include <sys/types.h> // 基本数据类型定义  
#include <sys/socket.h> // 套接字编程接口  
#include <netinet/in.h> // 定义IPv4地址  
#include <netinet/ip.h> // IP协议头  
#include <arpa/inet.h>  // 定义IP地址转换函数  
#include <time.h>       // 包含时间处理函数,但本程序中未使用  // 注意:这里的typedef使用方式不正确,它应该是一个类型别名,而不是一个函数指针  
// 正确的写法应该是:typedef struct sockaddr *SA; 去掉了括号和多余的空格  
typedef struct sockaddr *SA;  int main(int argc, char *argv[])  
{  // 创建一个UDP套接字  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);  if (-1 == sockfd) {  // 如果创建套接字失败,则打印错误信息并退出  perror("socket");  exit(1);  }  // 定义服务器地址结构体变量,并清零  struct sockaddr_in ser;  bzero(&ser, sizeof(ser)); // 使用bzero函数清零结构体// 设置服务器地址族为IPv4  ser.sin_family = AF_INET;  // 设置服务器端口号,使用htons函数进行网络字节序转换  ser.sin_port = htons(50000);  // 设置服务器IP地址,通过inet_addr函数将点分十进制字符串转换为网络字节序  ser.sin_addr.s_addr = inet_addr("192.168.203.128");  // 进入无限循环,持续发送数据并接收响应  while (1) {  // 定义一个发送缓冲区,并初始化为测试消息  char buf[512] = "hello,this is udp test";  // 向服务器发送数据  sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));  // 清零接收缓冲区,准备接收数据  bzero(buf, sizeof(buf));  // 接收服务器响应的数据,但注意这里传递了NULL作为地址和长度的参数,这是错误的  // 正确的做法应该是提供一个有效的sockaddr_in结构体变量和socklen_t类型的变量来接收客户端地址和长度  // 这里我们假设服务器和客户端在同一台机器上,且端口固定,因此可以省略这部分信息  // 但为了代码的健壮性和可移植性,建议总是检查来源地址  recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); // 这里需要修正  // 打印接收到的数据  printf("buf is %s\n", buf);  // 暂停一秒后再次发送  sleep(1);  }  // 注意:由于存在无限循环,这里的close和return语句实际上不会被执行  // 但为了代码的完整性,我还是保留了它们  // 如果需要退出程序,应该在循环内部添加适当的退出条件  close(sockfd); // 关闭套接字  return 0;      // 程序正常结束  
}  

练习:
1、根据以上知识点编写UDP测试程序,验证UDP协议的无链接性质。

1、将照片传输到客户端:

注意要将ip地址改成想要发送的ip地址

     ip: ifconfig

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(-1 == sockfd){perror("socket");exit(1);}// man 7 ip struct sockaddr_in ser,cli;bzero(&ser,sizeof(ser));bzero(&cli,sizeof(cli));ser.sin_family = AF_INET;// 大小端转化 host to net short ser.sin_port = htons(50000);//ser.sin_addr.s_addr = inet_addr("127.0.0.1");ser.sin_addr.s_addr = INADDR_ANY;int ret = bind(sockfd,(SA)&ser,sizeof(ser));if(-1 == ret){perror("bind");exit(1);}socklen_t len = sizeof(cli);int fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666);if(-1 == fd){perror("open");exit(1);}while(1){char buf[512]={0};int rd_ret = recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);if(0 == strcmp(buf,"^_^")){break;}write(fd,buf,rd_ret);bzero(buf,sizeof(buf));strcpy(buf,"go on");sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);}close(sockfd);close(fd);return 0;
}

clink.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);int main(int argc, char *argv[])
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(-1 == sockfd){perror("socket");exit(1);}struct sockaddr_in ser;bzero(&ser,sizeof(ser));ser.sin_family = AF_INET;// 大小端转化 host to net short ser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.203.128");int fd = open("/home/linux/1.png",O_RDONLY);if(-1 == fd){perror("open");exit(1);}char buf[512]={0};while(1){bzero(buf,sizeof(buf));int rd_ret = read(fd,buf,sizeof(buf));if(0==rd_ret){break;}sendto(sockfd,buf,rd_ret,0,(SA)&ser,sizeof(ser));bzero(buf,sizeof(buf));recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);}bzero(buf,sizeof(buf));strcpy(buf,"^_^");sendto(sockfd,buf,3,0,(SA)&ser,sizeof(ser));close(sockfd);close(fd);return 0;
}

3、将以上知识点融合,考虑如何实现一个基于UDP的聊天室程序。
    要求如下:
    1、要有注册过程,每个客户端必须在服务器端有注册信息。
    2、任意客户端发送的消息必须由服务器转发给所有在线客户端。
    3、任意客户端下线必须通知其他在线用户主机。
    
    服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct 
{TYPE type;char name[50];char context[128];}MSG;
typedef struct 
{struct sockaddr_in cli;int flag; // 0  free 1 occu
}LIST;
#define MAX 10
LIST list[MAX]={0};
int do_login(int sockfd,MSG* msg,struct sockaddr_in* cli)
{int i = 0 ;for(i=0;i<MAX;i++){if(1 == list[i].flag ){sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));}}for(i=0;i<MAX;i++){if(0 == list[i].flag ){list[i].flag =1;//list[i].cli = *cli;memcpy(&list[i].cli,cli,sizeof(*cli));break;}}return 0;
}int do_chat(int sockfd, MSG* msg,struct sockaddr_in*cli)
{int i = 0 ;for(i=0;i<MAX;i++){if(1 == list[i].flag && 0!=memcmp(&list[i].cli,cli,sizeof(*cli)) ){sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));}}
}
int do_logout(int sockfd, MSG* msg,struct sockaddr_in*cli)
{return 0;
}
int main(int argc, char *argv[])
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(-1 == sockfd){perror("socket");exit(1);}// man 7 ip struct sockaddr_in ser,cli;bzero(&ser,sizeof(ser));bzero(&cli,sizeof(cli));ser.sin_family = AF_INET;// 大小端转化 host to net short ser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("127.0.0.1");int ret = bind(sockfd,(SA)&ser,sizeof(ser));if(-1 == ret){perror("bind");exit(1);}socklen_t len = sizeof(cli);MSG msg;while(1){bzero(&msg,sizeof(msg));recvfrom(sockfd,&msg,sizeof(msg),0,(SA)&cli,&len);switch(msg.type){case CMD_LOGIN:do_login(sockfd,&msg,&cli);break;case CMD_LOGOUT:do_logout(sockfd,&msg,&cli);break;case CMD_CHAT:do_chat(sockfd,&msg,&cli);break;}}close(sockfd);return 0;
}


    客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct 
{TYPE type;char name[50];char context[128];}MSG;
int main(int argc, char *argv[])
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(-1 == sockfd){perror("socket");exit(1);}// man 7 ip struct sockaddr_in ser,cli;bzero(&ser,sizeof(ser));ser.sin_family = AF_INET;// 大小端转化 host to net short ser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("127.0.0.1");socklen_t len = sizeof(cli);MSG msg;char name[50]={0};printf("input name:");fgets(name,sizeof(name),stdin);name[strlen(name)-1]='\0';msg.type = CMD_LOGIN;strcpy(msg.name ,name);strcpy(msg.context,"login");sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));pid_t pid = fork();if(pid>0){while(1){bzero(&msg,sizeof(msg));recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL);    printf("%s:%s\n",msg.name,msg.context);}}else if(0==pid){while(1){printf("to all");    char buf[128]={0};strcpy(msg.name,name);msg.type = CMD_CHAT;fgets(msg.context,sizeof(msg.context),stdin);//#quitmsg.context[strlen(msg.context)-1]='\0';if(0==strcmp(msg.context,"#quit")){msg.type = CMD_LOGOUT;strcpy(msg.context,"CMD_LOGOUT");}sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));}}else {perror("fork");exit(1);}close(sockfd);return 0;
}


    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/43464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编译libmp3lame支持SSE指令

碰到的编译错误如下&#xff1a; lame/libmp3lame/vector/xmm_quantize_sub.c:72:18: warning: SSE vector return without SSE enabled changes the ABI [-Wpsabi]72 | const __m128 vec_fabs_mask _mm_loadu_ps(&fabs_mask._float[0]);| ^~~~~~~~…

【NLP】利用 RAG 模分块技术提升文档处理效能

将大型文档划分为较小的部分是一项至关重要但又复杂的任务&#xff0c;它对检索增强生成 (RAG) 系统的性能有重大影响。这些系统旨在通过结合基于检索和基于生成的方法&#xff0c;提高输出的质量和相关性。有效的分块&#xff0c;即将文档拆分为可管理的片段的过程&#xff0c…

音频demo:使用开源项目lame来将MP3数据解码出PCM数据

1、README 前言 本demo是使用开源项目lame来将MP3数据解码成PCM数据。&#xff08;环境&#xff1a;x86_64 Ubuntu16.04 64位&#xff09; 注&#xff1a;在下面【参考文章】中的第1篇里面提到解码出现过异常情况&#xff08;hip: bitstream problem, resyncing skipping xxx…

显示渲染-OSG框架解析

1.背景介绍 1.1 OSG介绍 OSG的全称&#xff1a;OpenSceneGraph&#xff0c;它是一个开放源码&#xff0c;跨平台的图形开发包&#xff0c;它为诸如飞行器仿真&#xff0c;游戏&#xff0c;虚拟现实&#xff0c;科学计算可视化这样的高性能图形应用程序开发而设计。 它基于场…

Springboot随机端口配置

网上所有地方都告诉你的错误配置: server: port: ${random.int[6008,6009]} 这才是正确配置: server: port: ${random.int(6008,6009)} 如果能解决你的问题请记得点赞&#xff01;让更多人知道&#xff01;

DNS缓存详解

目录 一、缓存分类 1. 客户端缓存&#xff08;以浏览器缓存为列&#xff09; 2. 操作系统缓存 3.本地hosts文件静态映射 二、DNS查找优先顺序 1.浏览器查找顺序 2.cmd ping查找顺序&#xff08;非浏览器&#xff09; 一、缓存分类 在一台终端上&#xff0c;DNS缓存可以…

Android 12系统源码_设备设置(一)Settings介绍

前言 Settings 类是一个用于访问和管理设备设置的关键类&#xff0c;而作为系统开发人员&#xff0c;经常需要用这个类来做一些系统设备设置&#xff0c;而Settings里面存在着好几个处理不同领域的设备设置类&#xff0c;那么如何才能结合自己的业务场景正确选择使用这些设备设…

微信小程序开发跳转京东,淘宝小程序

没有淘宝小程序&#xff0c;所以只能提示他复制链接网页打开 跳转京东小程序 获取京东小程序 京东小程序appId&#xff1a;wx91d27dbf599dff74 或者点开京东小程序&#xff0c;查看详情即可获取到京东的appid 店铺页面路径 店铺首页&#xff1a;pages/shop/index/index?…

《无所不能的JavaScript · prototype 原型链》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;欢迎多多交流。&am…

OpenWrt入门 (1) - 登录及ssh命令接入wifi

本文参考自: [OpenWrt 维基]在 OpenWrt 上启用 Wi-Fi 接入点 --- [OpenWrt Wiki] Enabling a Wi-Fi access point on OpenWrt 需要详细了解的小伙伴请看原文 基本概念 OpenWrt是适用于嵌入式设备的一个Linux发行版。 相对原厂固件而言&#xff0c;OpenWrt不是一个单一、静态…

企业如何从无序管理走向精益生产管理?

先来看看企业生产管理无序的弊端有哪些&#xff1f; 数据统计不及时&#xff1a;纸质生产工单&#xff0c;数据难统计&#xff0c;各业务环节问题难定位&#xff0c;影响车间生产效率。生产过程不透明&#xff1a;生产过程数据难监控&#xff0c;生产派工管理混乱&#xff0c;…

【前端】面试八股文——meta标签

【前端】面试八股文——meta标签 在HTML文档中&#xff0c;meta标签是一个关键但常被忽视的元素。它位于文档的<head>部分&#xff0c;用于提供关于HTML文档的元数据&#xff08;metadata&#xff09;。这些元数据不会直接显示在页面上&#xff0c;但对搜索引擎优化&…

web前端开发——标签一

今天我来针对web前端开发讲解标签一 Html标签_标题&段落&换行 注释标签&#xff1a;Ctrl/ Ctrl/ &#xff0c;用户可能会获取到注释标签 注释的原则: •和代码逻辑一致 •尽量使用中文 •正能量 标题标签&#xff1a;<h1></h1> h1-h6 标题标签有6…

C++线程锁std::mutex

基本用法 “mutex”是“mutual exclusion”的缩写&#xff0c;意思是互斥锁。互斥锁用于多线程编程中&#xff0c;以确保同一时间只有一个线程能够访问某一共享资源&#xff0c;从而避免数据竞争和不一致性。 std::mutex是最基本的互斥锁&#xff0c;用于保护共享数据。它有两…

Vue3框架搭建2:axios+typescript封装

仓库地址&#xff1a;https://github.com/buguniao5213/LuArch&#xff08;分支代码未上传&#xff0c;完整一系列后传一波&#xff0c;中途有需求可以再传&#xff09; 1、安装axios npm install axios2、创建文件 先创建一个文件夹&#xff1a; ├── src/ │ ├── …

51单片机-第二节-数码管

一、数码管介绍&#xff1a; 1.什么是数码管&#xff1f; 多个LED组合成8字显示器。 2.一位数码管的引脚&#xff08;只有一个8&#xff09;&#xff1a; 数码管的引脚为1-10&#xff0c;其中公共极为3,8&#xff0c;其余八位分别对应一个二极管&#xff0c;如下图&#xff…

Vue2 基础十Vuex

代码下载 Vuex 概述 组件之间共享数据的方式&#xff1a; 父组件向子组件传值&#xff0c;是以属性的形式绑定值到子组件&#xff08;v-bind&#xff09;&#xff0c;然后子组件用属性props接收。子组件向父组件传值&#xff0c;子组件用 $emit() 自定义事件&#xff0c;父组…

JavaScript--local storage存储的数组不可扩展的问题

数组扩展 问题解析解决办法总结进一步扩展原因 问题 下列代码中的points是从本地存储中获取到的数据&#xff0c;我想存储到一个Map并且新增元素的时候报错 let obj this.objectsManager._objects.get(obstacle.uuid);let points obj.track_points;this.dyObstacleTP.set(ob…

【大模型】大模型相关技术研究—微调

为什么要对大模型进行微调 1.成本效益&#xff1a; o 大模型的参数量非常大&#xff0c;训练成本非常高&#xff0c;每家公司都去从头训练一个自己的大模型&#xff0c;这个事情的性价比非常低。 2.Prompt Engineering 的局限性&#xff1a; o Prompt Engineering 是一种相…

视图库对接系列(GA-T 1400)十二、视图库对接系列(本级)人员数据推送

背景 人体和非机动车和机动车类似的,只是请求的参数不一样而已。人员数据推送 接入人员数据推送相对比较简单,我们只需要实现对应的接口就ok了。 具体如图: 有增删改查接口,目前的话 因为我们是做平台,我们只需要实现添加接口就可以了。 接口实现 service 层 /**** …