UDP协议 sendto 和 recvfrom 浅析与示例

news2025/1/9 15:52:31

UDP(user datagram protocol)用户数据报协议,属于传输层。

UDP是面向非连接的协议,它不与对方建立连接,而是直接把数据报发给对方。UDP无需建立类如三次握手的连接,使得通信效率很高。因此UDP适用于一次传输数据量很少、对可靠性要求不高的或对实时性要求高的应用场景。

UDP通信的过程:

服务端:

(1)使用函数socket(),生成套接字文件描述符;

(2)通过struct sockaddr_in 结构设置服务器地址和监听端口;

(3)使用bind() 函数绑定监听端口,将套接字文件描述符和地址类型变量(struct sockaddr_in )进行绑定;

(4)接收客户端的数据,使用recvfrom() 函数接收客户端的网络数据;

(5)向客户端发送数据,使用sendto() 函数向服务器主机发送数据;

(6)关闭套接字,使用close() 函数释放资源;

客户端:

(1)使用socket(),生成套接字文件描述符;

(2)通过struct sockaddr_in 结构设置服务器地址和监听端口;

(3)向服务器发送数据,sendto() ;

(4)接收服务器的数据,recvfrom() ;

(5)关闭套接字,close() ;

sockaddr 与 sockaddr_in 区别

一、sockaddr

sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:

/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;
 
/* This macro is used to declare the initial common members
   of the data types used for socket addresses, `struct sockaddr',
   `struct sockaddr_in', `struct sockaddr_un', etc.  */
 
#define	__SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
 
 
 
 
/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
   };

相关视频推荐

C++网络面试题:TCP/UDP应用场景分析,UDP如何实现可靠性设计

大厂面试复盘-UDP协议常见面试问题分享

7道面试题打通C/C++后端开发的技术脉络

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun579733396获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

二、sockaddr_in

sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:

/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */
 
    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr)
			   - __SOCKADDR_COMMON_SIZE
			   - sizeof (in_port_t)
			   - sizeof (struct in_addr)];
  };

sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)。

三、总结

二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。

sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。

sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。

例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
 
int main(int argc,char **argv)
{
    int sockfd = 0;
    struct sockaddr_in addr_in;
    struct sockaddr * addr;
 
    sockfd = socket(AF_INET, SOCK_STREAM, 0);  //获得fd
    bzero(&addr_in,sizeof(addr_in));  // 初始化结构体
    /*
     8008的主机字节序  小端字节序 0001 1111 0100 1000 = 8008
     8008的网络字节序  大端字节序 0100 1000 0001 1111 = 18463
    */
    addr_in.sin_port = htons(8008);
    addr_in.sin_family = AF_INET;  // 设置地址家族
    addr_in.sin_addr.s_addr = inet_addr("192.168.3.30");  //设置地址
    printf("sockaddr_in.sin_addr.s_addr = %d \n", addr_in.sin_addr.s_addr);
    printf("addr = %s \n", inet_ntoa(addr_in.sin_addr));
//    addr_in.sin_addr.s_addr = htonl(INADDR_ANY);  //设置地址
 
    printf("struct sockaddr size = %ld \n", sizeof (addr));
    printf("struct sockaddr_in size = %ld \n", sizeof (addr_in));
    addr = (struct sockaddr *)&addr_in;
//    bind(sockfd, (struct sockaddr *)&addr_in, sizeof(struct sockaddr));  /* bind的时候进行转化 */
    bind(sockfd, addr, sizeof(struct sockaddr));
    ... ...
    return 0;
}

题外话,两个函数 htons() 和 inet_addr()。

htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)

inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。

inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。比如:

printf("%s",inet_ntoa(mysock.sin_addr));

htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。

与htonl()和htons()作用相反的两个函数是:ntohl()和ntohs()。

sendto()

1 1 int sendto(int s, const void *buf, int len, unsigned int flags, 
2         const struct sockaddr *to, int tolen);

返回值说明:

成功则返回实际传送出去的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

  • s: socket描述符;

  • buf:UDP数据报缓存区(包含待发送数据);

  • len: UDP数据报的长度;

  • flags:调用方式标志位(一般设置为0);

  • to:  指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换);

  • tolen:to所指结构体的长度;

recvfrom()

1 int recvfrom(int s, void *buf, int len, unsigned int flags,
2           struct sockaddr *from, int *fromlen); 

返回值说明:

成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

  • s: socket描述符;

  • buf: UDP数据报缓存区(包含所接收的数据);

  • len: 缓冲区长度。

  • flags: 调用操作方式(一般设置为0)。

  • from: 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换);

  • fromlen:指针,指向from结构体长度值。

示例代码

服务端

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define MAXLINE 4096
#define UDPPORT 8001
#define SERVERIP "192.168.255.129"

using namespace std;

int main(){
    int serverfd;
    unsigned int server_addr_length, client_addr_length;
    char recvline[MAXLINE];
    char sendline[MAXLINE];
    struct sockaddr_in serveraddr , clientaddr;

    // 使用函数socket(),生成套接字文件描述符;
    if( (serverfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ){
        perror("socket() error");
        exit(1);
    }

    // 通过struct sockaddr_in 结构设置服务器地址和监听端口;
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(UDPPORT);
    server_addr_length = sizeof(serveraddr);

    // 使用bind() 函数绑定监听端口,将套接字文件描述符和地址类型变量(struct sockaddr_in )进行绑定;
    if( bind(serverfd, (struct sockaddr *) &serveraddr, server_addr_length) < 0){
        perror("bind() error");
        exit(1);
    }

    // 接收客户端的数据,使用recvfrom() 函数接收客户端的网络数据;
    client_addr_length = sizeof(sockaddr_in);
    int recv_length = 0;
    recv_length = recvfrom(serverfd, recvline, sizeof(recvline), 0, (struct sockaddr *) &clientaddr, &client_addr_length);
    cout << "recv_length = "<< recv_length <<endl;
    cout << recvline << endl;

    // 向客户端发送数据,使用sendto() 函数向服务器主机发送数据;
    int send_length = 0;
    sprintf(sendline, "hello client !");
    send_length = sendto(serverfd, sendline, sizeof(sendline), 0, (struct sockaddr *) &clientaddr, client_addr_length);
    if( send_length < 0){
        perror("sendto() error");
        exit(1);
    }
    cout << "send_length = "<< send_length <<endl;

    //关闭套接字,使用close() 函数释放资源;
    close(serverfd);

    return 0;
}

客户端

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define MAXLINE 4096
#define UDPPORT 8001
#define SERVERIP "192.168.255.129"

using namespace std;

int main(){
    int confd;
    unsigned int addr_length;
    char recvline[MAXLINE];
    char sendline[MAXLINE];
    struct sockaddr_in serveraddr;

    // 使用socket(),生成套接字文件描述符;
    if( (confd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ){
        perror("socket() error");
        exit(1);
    }

    //通过struct sockaddr_in 结构设置服务器地址和监听端口;
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(SERVERIP);
    serveraddr.sin_port = htons(UDPPORT);
    addr_length = sizeof(serveraddr);

    // 向服务器发送数据,sendto() ;
    int send_length = 0;
    sprintf(sendline,"hello server!");
    send_length = sendto(confd, sendline, sizeof(sendline), 0, (struct sockaddr *) &serveraddr, addr_length);
    if(send_length < 0 ){
        perror("sendto() error");
        exit(1);
    }
    cout << "send_length = " << send_length << endl;

    // 接收服务器的数据,recvfrom() ;
    int recv_length = 0;
    recv_length = recvfrom(confd, recvline, sizeof(recvline), 0, (struct sockaddr *) &serveraddr, &addr_length);
    cout << "recv_length = " << recv_length <<endl;
    cout << recvline << endl;

    // 关闭套接字,close() ;
    close(confd);

    return 0;
}

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

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

相关文章

Kyligence Zen 一站式指标平台体验——“绝对实力”的指标分析和管理工具——入门体验评测

&#x1f996;欢迎观阅本本篇文章&#xff0c;我是Sam9029 文章目录 前言Kyligence Zen 是什么Kyligence Zen 能做什么Kyligence Zen 优势在何处 正文注册账号平台功能模块介绍指标图表新建指标指标模板 目标仪表盘数据设置 实际业务体验---使用官网数据范例使用流程归因分析指…

MySQL --- 多表设计

关于单表的操作(包括单表的设计、单表的增删改查操作)我们就已经学习完了。接下来我们就要来学习多表的操作&#xff0c;首先来学习多表的设计。 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构…

ChatGPT-4怎么对接-ChatGPT-4强化升级了哪些功能

ChatGPT-4怎么使用 使用ChatGPT-4&#xff0c;需要通过OpenAI的API接口来对接ChatGPT-4。OpenAI是一个人工智能公司&#xff0c;为开发者提供多个API接口&#xff0c;包括自然语言处理&#xff0c;图像处理等。ChatGPT-4是OpenAI开发的最新版本的聊天式对话模型&#xff0c;可…

React antd Form item「受控组件与非受控组件」子组件 defaultValue 不生效等问题总结

一、为什么 Form.Item 下的子组件 defaultValue 不生效&#xff1f; 当你为 Form.Item 设置 name 属性后&#xff0c;子组件会转为受控模式。因而 defaultValue 不会生效。你需要在 Form 上通过 initialValues 设置默认值。name 字段名&#xff0c;支持数组 类型&#xff1a;N…

2.存储器层次系统

存储器 随机访问存储器 RAM&#xff08;随机存储器&#xff09; SRAM 双稳态触发器&#xff0c;有电就保持不变&#xff0c;干扰消除后时会恢复到稳定值&#xff0c;晶体管多因此密集度低 DRAM 每个位存储为对一个电容的充电&#xff0c;对干扰敏感&#xff0c;漏电所以需要刷…

静态数码管

静态数码管 1、简介工作方式数码管静态显示原理 2、硬件设计3、软件设计4、 1、简介 一般共阳极数码管更为常用 好处&#xff1a;将驱动数码管的工作交到公共端&#xff08;一般接驱动电源&#xff09;&#xff0c;加大驱动电源的功率自然要比加大IC芯片I/O口的驱动电流简单许…

【python 生成器】零基础也能轻松掌握的学习路线与参考资料

一、学习路线 了解生成器的概念和作用 首先&#xff0c;需要明确生成器的概念和作用&#xff0c;生成器是一种特殊的迭代器&#xff0c;它可以在循环中逐个地产生值&#xff0c;而不是一次性将所有的值产生出来。它的作用是使程序更加高效&#xff0c;达到节省内存等的效果。…

Linux 入门

文章目录 一、概述二、安装CentOS下载地址VMware下载地址 三、linux文件与目录结构Linux系统中一切皆文件Linux目录结构 四、VI/VIM 编辑器vi/vim是什么一般模式常用语法键盘图编辑模式指令模式 五、网络配置六、远程登陆七、系统管理Linux 中的进程和服务service 服务管理chkc…

几种常见的电源防反接电路

电源防反接&#xff0c;也即是防止电源的正负极搞反而导致电路损坏&#xff0c;例如你采用的是标准的DC口&#xff0c;那么没什么必要加入此种电路。而如果采用的是非常规的&#xff0c;如自定义的接插件等&#xff0c;那么就很有必要了。 举个例子&#xff1a;小编以前就采用…

企业在线制作帮助中心,选择:语雀、石墨、Baklib哪个好?

在当今互联网时代&#xff0c;越来越多的企业开始将帮助中心建设在线化。在线帮助中心的好处不仅可以提高用户的使用体验&#xff0c;也可以提高企业的工作效率。然而&#xff0c;选择一个合适的在线制作帮助中心工具却并不是一件容易的事情。在众多的在线制作帮助中心工具中&a…

Python3 入门教程||Python3 SMTP发送邮件||Python3 多线程

Python3 SMTP发送邮件 在Python3 中应用的SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。 python的 smtplib 提供了一种很方便的途径发送电子邮件。它对…

[cryptoverse CTF 2023] crypto部分

没打,完事作作题. Warmup 1 Decode the following ciphertext: GmvfHt8Kvq16282R6ej3o4A9Pp6MsN. Remember: CyberChef is your friend. Another great cipher decoding tool is Ciphey. 热身一下就凉,问了别人,用ciphey说是能自动解,但是安装报错 rot13base58 这个没有自动的…

JavaCollection集合:概述、体系特点、常用API、遍历方式

一、集合概述 集合和数组都是容器 数组 特点&#xff1a;数组定义完成并启动后&#xff0c;类型确定、长度固定。 劣势&#xff1a;在进行增删数据操作的时候&#xff0c;数组是不太合适的&#xff0c;增删数据都需要放弃原有数组或者移位。 使用场景&#xff1a;当业务数…

JMeter 常用的几种断言方法,你会了吗?

JMeter是一款常用的负载测试工具&#xff0c;通过模拟多线程并发请求来测试系统的负载能力和性能。在进行性能测试时&#xff0c;断言&#xff08;Assertion&#xff09;是非常重要的一部分&#xff0c;可以帮助我们验证测试结果的正确性。下面介绍JMeter常用的几种断言方法。 …

MySQL 运算符解析

1.算术运算符 算术运算符主要用于数学运算&#xff0c;其可以连接运算符前后的两个数值或表达式&#xff0c;对数值或表达式进行加 &#xff08;&#xff09;、减&#xff08;-&#xff09;、乘&#xff08;*&#xff09;、除&#xff08;/&#xff09;和取模&#xff08;%&…

K8S:K8S部署常见错误及解决方法

目录 1、node节点kubelet服务起不来 2、安装cni网络插件时 kubectl get node master和node一直noready①有延时&#xff0c;需要等待10分钟左右&#xff0c;超过15分钟则有问题 3、部署报错kubectl get nodes No resources found 4、k8s部署报错error&#xff1a;kubectl ge…

OpenCV 直方图统计函数 cv::calcHist算是彻底弄明白了

参数说明 void calcHist( const Mat* images, int nimages,const int* channels, InputArray mask,OutputArray hist, int dims, const int* histSize,const float** ranges, bool uniform true, bool accumulate false );images 图像数组。每个图像的大小要一致&#xff0c…

龙的画法图片

由龙老师画素描中国龙的方法,大概可以遵循以下步骤: 确定龙的姿态和比例:在纸上简单地画出龙的基本形状和姿态,包括身体的长度,颈部、腿和尾巴的位置和比例关系。 添加细节:在基本形状的基础上,开始添加一些细节,如龙的头部、眼睛、鼻子、嘴巴、爪子等。注意要保持姿态和比例…

来,花半小时我带你入门vue

半小时入门vue 前言&#xff08;&#xff01;important&#xff09;学习vue的前提什么是vue&#xff1f;vue的引入方式实例化一个对象和创建一个对象实例化一个vue对象模板语法1.插值表达式2.v-text和v-html指令3.v-bind指令4.v-on指令5.v-model指令6.v-if和v-show指令7.v-for指…

浅谈一下接口工具(jmeter、postman、swagger等)

一、接口都有哪些类型&#xff1f; 接口一般分为两种&#xff1a;1.程序内部的接口 2.系统对外的接口 系统对外的接口&#xff1a;比如你要从别的网站或服务器上获取资源或信息&#xff0c;别人肯定不会把 数据库共享给你&#xff0c;他只能给你提供一个他们写好的方法来获取…