linux中UDP编程

11 篇文章 0 订阅
10 篇文章 0 订阅

在前面的文件中,我们介绍了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中,完成一个基本的通信涉及到的几个函数如下:

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就行了

recvfrom(sockfd, buf, len, flags, NULL, NULL);

但是当我们需要知道发送者的IP信息时,就需要指定这两个参数,用来或者发送者的IP信息和IP的数据长度

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信息不对,正确的使用方式为:

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),其原型如下:

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后,配置一下套接字,允许进行发送广播消息,上代码

int set_broadcast = 1;
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &set_broadcast,sizeof(set_broadcast));

四、注意事项

1、在某些情况下,我们的服务端的程序需要同时使用单播、组播和广播的方式,且一般的程序都会使用指定的端口。这样在实际的使用过程中,程序运行经常性的会遇到这样的问题:

Address already in use

例如我们的服务端通过广播的方式在网络上广播了自己的存在,告知其他主机自己的IP地址信息和与自己通信的方式,在广播完成后, 程序会建立一个UDP的单播客户端,等待感兴趣的客户端发送信息。此时,在建立客户端的时候,往往会报上述的错误。

解决方法如下:(允许端口重用)

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来实现。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值