• 正文
  • 相关推荐
申请入驻 产业图谱

一种通过udp进行无确认ip的双向的通信

02/27 10:19
777
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论
udp是一种不可靠的通信,但是有些时候还是会有使用。今天分享一个示例:主体逻辑,一个端口广播地址,接收到ip地址数据后,其他端口基于这个ip进行bind绑定,最后通信,这样可以保证我们后续继续增加端口交互时候不需要关注ip地址绑定的问题。

欢迎关注微信公众号:羽林君,或者添加作者个人微信:become_me

主要原理介绍

低通信频率端口进行服务端IP信息udp广播,接收端是不固定IP监听,监听主机任意IP地址的特定端口

接收到广播通道的ip地址后,与特定IP、port建立tcp或者udp双向高频率通信。

下图是基于 UDP 的 Socket 函数调用过程:

只有接收的时候需要bind ip和端口

socket 监听所有ip 特定端口代码:

#define?PORT?6000
bzero(&adr_inet,?sizeof(adr_inet));
adr_inet.sin_family?=?AF_INET;
adr_inet.sin_addr.s_addr?=?htonl(INADDR_ANY);
adr_inet.sin_port?=?htons(port);
ret?=?bind(cfd,?(struct?sockaddr?*)&addr,?sizeof(addr));

socket绑定的ip为INADDR_ANY 的说明:

socket INADDR_ANY 监听0.0.0.0地址 socket只绑定端口让路由表决定传到哪个ip

其中INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
如果指定ip地址为通配地址(INADDR_ANY),那么内核将等到套接字已连接(TCP)或已在套接字上发出数据报时才选择一个本地IP地址。
一般情况下,如果你要建立网络服务器,则你要通知服务器操作系统:请在某地址 xxx.xxx.xxx.xxx上的某端口 yyyy上进行侦听,并且把侦听到的数据包发送给我。这个过程,你是通过bind()系统调用完成的。——也就是说,你的程序要绑定服务器的某地址,或者说:把服务器的某地址上的某端口占为已用。服务器操作系统可以给你这个指定的地址,也可以不给你。

如果你的服务器有多个网卡,而你的服务(不管是在udp端口上侦听,还是在tcp端口上侦听),出于某种原因:可能是你的服务器操作系统可能随时增减IP地址,也有可能是为了省去确定服务器上有什么网络端口(网卡)的麻烦 —— 可以要在调用bind()的时候,告诉操作系统:“我需要在 yyyy 端口上侦听,所以发送到服务器的这个端口,不管是哪个网卡/哪个IP地址接收到的数据,都是我处理的。”这时候,服务器则在0.0.0.0这个地址上进行侦听。无论连接哪个ip都可以连上的,只要是往这个端口发送的所有ip都能连上。

示例代码:

data_send.c 在端口9001进行ip地址的udp广播以及读取终端数据广播到7000端口

#include?<stdio.h>
#include?<stdlib.h>
#include?<unistd.h>
#include?<string.h>
#include?<netinet/in.h>
#include?<arpa/inet.h>
#include?<sys/socket.h>
#include?<sys/types.h>
#include?<errno.h>
#include?<pthread.h>
#include?<signal.h>

#define?IP?"127.0.0.1"
#define?PORT?6000
#define?DATA_PORT?9001

//?gcc?data_send.c?-o?data_send?-pthread

int?cfd?=?-1;
//接收线程函数
void?*receive(void?*pth_arg)
{
????int?ret?=?0;
????char?name_data[3]?=?{0};
????struct?sockaddr_in?addr0?=?{0};
????int?addr0_size?=?sizeof(addr0);
????//从对端ip和端口号中接收消息,指定addr0用于存放消息
????while?(1)
????{
????????bzero(name_data,?sizeof(name_data));
????????ret?=?recvfrom(cfd,?name_data,?sizeof(name_data),?0,?(struct?sockaddr?*)&addr0,?&addr0_size);
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"recv?failed",?strerror(errno));
????????????exit(-1);
????????}
????????else?if?(ret?>?0)
????????{
????????????printf("nname?=?%s?",?name_data);?//打印对方的消息和端口号
????????????printf("ip?%s,port?%d?n",?inet_ntoa(addr0.sin_addr),?ntohs(addr0.sin_port));
????????}
????}
}
void?*data_send(void?*pth_arg)
{
????int?ret?=?0;
????char?data[]?=?"IP?address";
????struct?sockaddr_in?addr0?=?{0};
????addr0.sin_family?=?AF_INET;????????????//设置tcp协议族
????addr0.sin_port?=?htons(DATA_PORT);??????????//设置端口号
????addr0.sin_addr.s_addr?=?htonl(INADDR_ANY);?//设置ip地址
????//发送消息
????while?(1)
????{
????????ret?=?sendto(cfd,?(void?*)data,?sizeof(data),?0,?(struct?sockaddr?*)&addr0,?sizeof(addr0));
????????sleep(1);
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"sendto?failed",?strerror(errno));
????????????exit(-1);
????????}
????}
}

int?main()
{
????int?ret?=?-1;
????//创建tcp/ip协议族,指定通信方式为无链接不可靠的通信
????cfd?=?socket(AF_INET,?SOCK_DGRAM,?0);
????if?(-1?==?cfd)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"socket?failed",?strerror(errno));
????????exit(-1);
????}

????//进行端口号和ip的绑定
????struct?sockaddr_in?addr;
????addr.sin_family?=?AF_INET;???//设置tcp协议族
????addr.sin_port?=?htons(PORT);?//设置端口号
????addr.sin_addr.s_addr?=?inet_addr(IP);?//设置ip地址
????ret?=?bind(cfd,?(struct?sockaddr?*)&addr,?sizeof(addr));

????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"bind?failed",?strerror(errno));
????????exit(-1);
????}

????//创建线程函数,用于处理数据接收
????pthread_t?id,data_send_id;
????ret?=?pthread_create(&id,?NULL,?receive,?NULL);
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"pthread_create?failed",?strerror(errno));
????????exit(-1);
????}
????//?pthread_join(id,NULL);
????ret?=?pthread_create(&data_send_id,?NULL,?data_send,?NULL);
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"pthread_create?failed",?strerror(errno));
????????exit(-1);
????}

????struct?sockaddr_in?addr0;
????addr0.sin_family?=?AF_INET;????????????//设置tcp协议族
????addr0.sin_port?=?htons(7000);??????????//设置端口号
????addr0.sin_addr.s_addr?=?inet_addr(IP);?//设置ip地址

????char?name_send[3]?=?{0};
????//发送消息
????while?(1)
????{
????????bzero(name_send,?sizeof(name_send));
????????printf("send?name:");
????????scanf("%s",?name_send);

????????//发送消息时需要绑定对方的ip和端口号
????????ret?=?sendto(cfd,?(void?*)name_send,?sizeof(name_send),?0,?(struct?sockaddr?*)&addr0,?sizeof(addr0));
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"accept?failed",?strerror(errno));
????????????exit(-1);
????????}
????}
????return?0;
}

data_process.c 进行端口9001的ip数据的捕获,当接收到ip数据后,绑定广播的ip地址进行数据的收发,这里用的是udp接收大家也可以试试tcp交互。

#include?<stdio.h>
#include?<stdlib.h>
#include?<unistd.h>
#include?<string.h>
#include?<netinet/in.h>
#include?<arpa/inet.h>
#include?<sys/socket.h>
#include?<sys/types.h>
#include?<errno.h>
#include?<pthread.h>
#include?<signal.h>

#define?IP?"127.0.0.1"
#define?PORT?7000
#define?DATA_PORT?9001
//?typedef?uint32_t?in_addr_t;

//?gcc?data_process.c?-o?data_process?-pthread
int?cfd?=?-1,data_fd?=?-1;
uint32_t?receive_ip?=?-1;
void?*receive(void?*pth_arg)
{
????int?ret?=?0;
????char?name_data[3]?=?{0};
????struct?sockaddr_in?addr0?=?{0};
????int?addr0_size?=?sizeof(addr0);
????while?(1)
????{
????????printf("receive:");
????????bzero(name_data,?sizeof(name_data));
????????ret?=?recvfrom(cfd,?name_data,?sizeof(name_data),?0,?(struct?sockaddr?*)&addr0,?&addr0_size);
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"recv?failed",?strerror(errno));
????????????exit(-1);
????????}
????????else?if?(ret?>?0)
????????{
????????????printf("nname?=?%s?",?name_data);
????????????printf("ip?%s,port?%d?n",?inet_ntoa(addr0.sin_addr),?ntohs(addr0.sin_port));
????????}
????}
}
void?*data_receive(void?*pth_arg)
{
????int?ret?=?0;
????char?name_data[10]?=?{0};
????struct?sockaddr_in?addr0?=?{0};
????int?addr0_size?=?sizeof(addr0);
????while?(1)
????{
????????bzero(name_data,?sizeof(name_data));
????????ret?=?recvfrom(data_fd,?name_data,?sizeof(name_data),?0,?(struct?sockaddr?*)&addr0,?&addr0_size);
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"recv?failed",?strerror(errno));
????????????exit(-1);
????????}
????????else?if?(ret?>?0)
????????{
????????????printf("nname?=?%s?",?name_data);
????????????printf("ip?%s,port?%d?n",?inet_ntoa(addr0.sin_addr),?ntohs(addr0.sin_port));
????????????receive_ip?=?addr0.sin_addr.s_addr;
????????????char?buf[20]?=?{?0?};
????????????inet_ntop(AF_INET,?&receive_ip,?buf,?sizeof(buf));
????????????printf("receive_ip?ip?=?%s?",?buf);
????????????//?printf("receive_ip?ip?=?%s?",?inet_ntop(receive_ip));
????????????break;
????????}
????}
}
int?main()
{
????int?ret?=?-1;
????data_fd?=?socket(AF_INET,?SOCK_DGRAM,?0);
????if?(-1?==?data_fd)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"socket?failed",?strerror(errno));
????????exit(-1);
????}

????struct?sockaddr_in?addr;
????addr.sin_family?=?AF_INET;????????????//设置tcp协议族
????addr.sin_port?=?htons(DATA_PORT);??????????//设置端口号
????addr.sin_addr.s_addr?=?inet_addr(IP);?//设置ip地址
????ret?=?bind(data_fd,?(struct?sockaddr?*)&addr,?sizeof(addr));
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"bind?failed",?strerror(errno));
????????exit(-1);
????}????
????pthread_t?receive_id;
????ret?=?pthread_create(&receive_id,?NULL,?data_receive,?NULL);
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"pthread_create?failed",?strerror(errno));
????????exit(-1);
????}
????pthread_join(receive_id,NULL);

????cfd?=?socket(AF_INET,?SOCK_DGRAM,?0);
????if?(-1?==?cfd)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"socket?failed",?strerror(errno));
????????exit(-1);
????}

????struct?sockaddr_in?addr1;
????addr1.sin_family?=?AF_INET;????????????//设置tcp协议族
????addr1.sin_port?=?htons(PORT);??????????//设置端口号
????addr1.sin_addr.s_addr?=?receive_ip;?//设置ip地址
????char?buf[20]?=?{?0?};
????inet_ntop(AF_INET,?&receive_ip,?buf,?sizeof(buf));
????printf("ip?=?%s?",?buf);

????ret?=?bind(cfd,?(struct?sockaddr?*)&addr1,?sizeof(addr1));
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"bind?failed",?strerror(errno));
????????exit(-1);
????}

????pthread_t?id;
????ret?=?pthread_create(&id,?NULL,?receive,?NULL);
????if?(-1?==?ret)
????{
????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"pthread_create?failed",?strerror(errno));
????????exit(-1);
????}
????pthread_join(id,NULL);

????struct?sockaddr_in?addr0;
????addr0.sin_family?=?AF_INET;????????????//设置tcp协议族
????addr0.sin_port?=?htons(6000);??????????//设置端口号
????addr0.sin_addr.s_addr?=?inet_addr(IP);?//设置ip地址

????char?name_send[3]?=?{0};
????while?(1)
????{
????????bzero(name_send,?sizeof(name_send));
????????printf("send?name:");
????????scanf("%s",?name_send);

????????ret?=?sendto(cfd,?(void?*)name_send,?sizeof(name_send),?0,?(struct?sockaddr?*)&addr0,?sizeof(addr0));
????????if?(-1?==?ret)
????????{
????????????fprintf(stderr,?"%d,?%s?:%s",?__LINE__,?"accept?failed",?strerror(errno));
????????????exit(-1);
????????}
????}
????return?0;
}

一个终端捕获数据,sudo tcpdump -i lo portrange 5000-8000 -vv -XX -nn,另外两个终端进行数据交互

结语

这就是我自己的一些udp设计思路的分享。如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。


作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

一个程序员,喜欢写文章,还喜欢打篮球,也喜欢吉他钢琴的驳杂之人。日常更新自己,分享包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!