该文章对萌新非常友善,值得一看
其实网上使用 htonl(INADDR_ANY) 绑定网络信息的udp编程代码只能大致说明UDP的交互流程;不能体现在实战中UDP的用法及细节,下面聊一聊udp在实际应用中的用法,帮助萌新梳理思路:
首先了解这两个函数:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数:
sockfd:套接字
buf:存放接收数据的缓存区
len:接收长度
flags:一般为0,不做详细解释,自信查询手册。
src_addr:对端的网络信息
addrlen:网络信息结构长度
接收到数据后,发送数据的源网络信息就会被保存在src_addr中,可以通过inet_ntoa(src_addr->sin_addr)获得对端ip; src_addr->sin_port获得对端端口号
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
sockfd:套接字
buf:存放发送数据的缓存区
len:发送长度
flags:一般为0,不做详细解释,自信查询手册。
dest_addr:对端的网络信息
addrlen:网络信息结构长度
接下来直接上代码,两台机器的ip为192.168.3.138和192.168.3.250 。
192.168.3.138的udp代码:
#include
#include
#include
#include
#include
#include
#include
//该程序可以接收任意发往本机的数据,利用sendto将信息发送给192.168.3.250
int main(int argc, const char **argv)
{
int fd = 0;
struct sockaddr_in sin;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("create socket error !");
return;
}
//绑定本地地址,用于接收数据
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.0.138");
bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
while(1)
{
//设置目的端的网络信息,通过第三个参数告诉sendto发给谁!
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_port = htons(8888);
client.sin_addr.s_addr = inet_addr("192.168.0.250");
ret = sendto(fd, data, len, 0, (struct sockaddr *)&client, sizeof(struct sockaddr_in));
if (ret < 0)
{
perror("send to heart pack erro : ");
return;
}
//用于接收数据,套接字fd已经绑定了本地的网络信息,于是可以接收发给本机的数据(不仅仅接收192.168.3.250的信息)
struct sockaddr_in src;
uint8_t buf[1024] = {0};
socklen_t len = sizeof(struct sockaddr_in);
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&src, &len);
//打印发给本机的源ip地址和端口号
printf("src ip : %s, src port : %d\n", inet_ntoa(src->sin_addr),src->sin_port);
}
}
192.168.3.250的代码:
#include
#include
#include
#include
#include
#include
#include
//该程序可以接收任意发往本机的数据,利用sendto将信息发送给192.168.3.250
int main(int argc, const char **argv)
{
int fd = 0;
struct sockaddr_in sin;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("create socket error !");
return;
}
//绑定本地地址,用于接收数据
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.0.250");
bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
while(1)
{
//用于接收数据,套接字fd已经绑定了本地的网络信息,于是可以接收发给本机的数据(不仅仅接收192.168.3.138的信息)
struct sockaddr_in src;
uint8_t buf[1024] = {0};
socklen_t len = sizeof(struct sockaddr_in);
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&src, &len);
//打印发给本机的源ip地址和端口号
printf("src ip : %s, src port : %d\n", inet_ntoa(src->sin_addr),src->sin_port);
//设置目的端的网络信息,通过第三个参数告诉sendto发给谁!
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_port = htons(8888);
client.sin_addr.s_addr = inet_addr("192.168.0.138");
ret = sendto(fd, data, len, 0, (struct sockaddr *)&client, sizeof(struct sockaddr_in));
if (ret < 0)
{
perror("send to heart pack erro : ");
return;
}
}
}
上述代码应该已经很明白了,所以理论上来说udp没有所谓的服务器,客户端之说;上述代码的通讯方式类似于点对点通讯,发送的数据只能被指定的ip地址收到。
既然这样为什么都说udp类似于广播呢?这是因为udp与通讯双方没有做连接动作,udp只是指定地址把数据散发出去,这个时候其他udp都可以接收这个数据,但是由于有指定地址,所以只有指定的地址才能接收该信息。类似于spi总线,主设备发送信号以后,所有从设备都能收到信号,从设备会判断是不是给自己的,如果不是就忽略。