UDP协议 sendto 和 recvfrom 浅析与示例 |
您所在的位置:网站首页 › udp_mem参数 › UDP协议 sendto 和 recvfrom 浅析与示例 |
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 办公设备维修网 版权所有 豫ICP备15022753号-3 |