UDP协议 sendto 和 recvfrom 浅析与示例 您所在的位置:网站首页 太原房产吧 UDP协议 sendto 和 recvfrom 浅析与示例

UDP协议 sendto 和 recvfrom 浅析与示例

2023-06-03 12:40| 来源: 网络整理| 查看: 265

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 中定义,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或#include 中定义,该结构体解决了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 #include #include #include 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 #include #include #include #include #include #include #include #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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有