大家好,又见面了,我是你们的朋友全栈君。
在前面的文件中,我们介绍了linux网络编程中与IP相关的知识和常用的函数总结,本文针对具体的UDP通信,来详细的介绍UDP通信的使用,包括UDP通信中的点对点通信,多播,广播等。
一、UDP通信中服务端和客户端的基本编程框架
与TCP相比较,UDP是面向无连接的通信方式,不需要connect、listen、accept等函数操作,不用维护TCP的连接、断开等状态。具体通信流程如下所示:
上面的通信过程还是比较清晰的,在实际的使用过程中,有几点需要注意下:
1、我们在编写服务端UDP程序时,bind是一个必须的步骤,这样系统才能知道我们程序recvfrom想从哪里或者哪个端口得到数据,如果没有这个不走,默认是无法收到数据的。当然,在我们服务端创建socket后,主动往外发送一个数据,这样即使我们不进行绑定,我们依然可以收到数据,这只是系统通过我们的发送,自动的绑定了一个端口,这个并不是我们想要的,实际的使用中,也并不推荐这种方式。
2、在上面的通信框架中,客户端并没有使用bind的操作,确实如此,因为客户端一般作为通信的发起者,都是主动往外发送数据,如1中的描述,这个过程由系统聪明的帮我们记录的端口信息,当服务端有数据回复的时候,系统就知道这个数据该转发给哪个端口了。但是,并不是说我们就不能主动的进行bind的操作。
3、关于服务端的bind操作,在存在组播,多播等多种通信方式的情况下,也还有一些需要注意的点,这个我们在下面的章节中描述
二、UDP通信的基本函数说明
在UDP中,完成一个基本的通信涉及到的几个函数如下:
代码语言:javascript复制int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
int close(int fd);
以上几个函数基本能做到 ”看名知意“,但是有一个小细节(也可以说是一个小坑)需要特别的注意:sendto中的addrlen参数很好理解,就是struct sockaddr参数的长度,一般在使用的过程中也不会有什么疑问,但是我们在使用recvfrom时,就需要注意addrlen这个参数了,如果我们不需要关心发送者的IP信息,填NULL就行了
代码语言:javascript复制recvfrom(sockfd, buf, len, flags, NULL, NULL);
但是当我们需要知道发送者的IP信息时,就需要指定这两个参数,用来或者发送者的IP信息和IP的数据长度
代码语言:javascript复制uint32_t addr_size;
struct sockaddr_in addr
recvfrom(socket, msg, msg_len, 0, (struct sockaddr *)&addr, &addr_size);
粗看上面的代码并没有什么问题,正常的理解就是addr中存在发送者的IP信息,addr_size存放addr数据的长度,但是,在实际使用中,这样调用后,我们打印addr中的信息,确实一个错误的IP信息或者0.0.0.0这样的地址信息,这是什么原因呢,在那个男人的中的描述,有如下的一段话
总结来说,就是我们必须初始化addr_size的长度,如果设置的长度比addr中的长度短,则会发生截断,获取到的IP信息不对,正确的使用方式为:
代码语言:javascript复制uint32_t addr_size = sizeof(struct sockaddr_in);
struct sockaddr_in addr
recvfrom(socket, msg, msg_len, 0, (struct sockaddr *)&addr, &addr_size);
这样我们才能正确的获取到IP信息。
三、UDP中组播的使用
单播和广播是两个极端,要么对一个主机进行通信,要么对整个局域网上的主机进行通信。但是我们在实际的使用中,通常只是某些主机对通信数据感兴趣,而不是整个局域网上的所有主机都需要这个数据,这种情况就需要组播登场了。
3.1、组播中的IP地址
组播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:
1、局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。·
2、预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。
3、管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。
3.2、组播的使用
组播在基本UDP编程框架的基础上,使用setsockopt()函数和getsockopt()函数来实现,需要设置IP层的相关参数(第二个参数为 IPPROTO_IP),其原型如下:
代码语言:javascript复制int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
常见组播设置选项如下:
其中:
选项IP_MULTICASE_TTL:设置超时时间,其值optval的设置范围为0-255
选项IP_MULTICAST_IF:设置组播的默认默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据
选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP:加入或者退出一个组播组其参数为一个结构体
使用组播的一个基本编程流程如下:
3.3 使用组播的服务端和客户端例子
(TBD)
三、UDP中广播的使用
UDP广播与普通的UDP通信区别不是很大,如果需要发送广播消息时,只需要在创建完socket后,配置一下套接字,允许进行发送广播消息,上代码
代码语言:javascript复制int set_broadcast = 1;
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &set_broadcast,sizeof(set_broadcast));
四、注意事项
1、在某些情况下,我们的服务端的程序需要同时使用单播、组播和广播的方式,且一般的程序都会使用指定的端口。这样在实际的使用过程中,程序运行经常性的会遇到这样的问题:
代码语言:javascript复制Address already in use
例如我们的服务端通过广播的方式在网络上广播了自己的存在,告知其他主机自己的IP地址信息和与自己通信的方式,在广播完成后, 程序会建立一个UDP的单播客户端,等待感兴趣的客户端发送信息。此时,在建立客户端的时候,往往会报上述的错误。
解决方法如下:(允许端口重用)
代码语言:javascript复制int on = 1;
ret = setsockopt(udp_net_sta.socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
if (ret < 0)
{
perror("socket set SO_REUSEADDR failed");
}
2、服务端程序,在创建完socket后,有一个bind的操作,对应绑定的端口,没有疑问,但是在选择绑定的IP地址时,一般我们会选择INADDR_ANY,这样不会有什么问题,单播和组播数据都能正常的收到,但是如果我们这边指定了一个固定的IP地址,就只能收到这个IP地址的数据了,如果同样需要实现单播,组播等功能,就需要创建多个socket来实现。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/195954.html原文链接:https://javaforall.cn