1.UDP概述
UDP(用户数据报协议)是一种简单的无连接的传输层协议,提供面向事物的简单的不可靠的服务,不同于TCP提供的面向连接的可靠字节流。虽然UDP是不可靠的服务,但是很多场合还是很适用UDP协议的
常见的UDP编写的应用程序有:DNS(域名系统),NFS(网络文件系统)和SNMP(简单网络管理协议)。
2.UDP的标准收发函数
#include<sys/socket.h>
ssize_t recvfrom(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen);
ssize_t sendto(int sockfd,const void *buff,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen);
//均返回:若成功则返回读或写的字节数,若出错则返回-1
这俩个函数的前三个参数等同与recv/send函数的前三个参数:套接字描述符,指向读入或写出的缓冲区,和读写的字节数
flags参数则可以改变收发函数的一些属性,这里我就不具体介绍了,一般将其设为0
recvfrom的最后俩个参数类似于accept的最后俩个参数,返回其中套接字地址的内容告诉我们是谁发的数据
sendto的to参数指向含有数据报接收者的协议地址的套接字地址结构(需要广播或单播的地址),其大小由addrlen参数指定
3.UDP的单播
单播的特点为:单播IP数据报仅由通过目的IP地址指定的单个主机接收,子网上的其他主机都不会受到任何影响
向一个单播地址发送UDP数据报时所发生的步骤为:
首先假设子网地址为192.168.34,此子网下有俩台IP地址分别为192.168.34.1和192.168.34.2的主机
(1)假设sendto往IP为192.168.34.2端口为3333发送数据报,此IP数据报在以太网下会激活ARP将目的以太网地址(192.168.34.2)映射成相应的以太网地址(物理地址),然后将目的地址为此地址的帧发出去
(2)在192.168.34子网下的主机的以太网接口看到该帧的时候都会与自己的以太网地址对比,如果不一致,该接口就会忽略该帧(也就是说单播不会对主机造成任何的额外开销,因为忽略他们的是接口而不是主机)
(3)直到IP为192.168.34.2的主机看到该帧时,比较发现相同,该接口读入整个帧,IP层处理他时会比较目的IP地址和自身IP地址,同样UDP层会检查目的端口和该进程端口,随后会将数据报置于相应的接收队列,这就是UDP单播的全过程
(4)单播的实例代码
server端
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
char *ip="192.168.34.2"; //目的主机IP地址
char *port1 = "2000"; //目的主机端口号
int port = atoi(port1);
int ret = 0;
int udpfd;
char buf[BUFFER_SIZE]={
"hello,world"
};
struct sockaddr_in address; //目的主机地址
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);
udpfd = socket(PF_INET,SOCK_DGRAM,0); //使用UDP协议
assert(udpfd >= 0);
sendto(udpfd,buf,BUFFER_SIZE-1,0,(struct sockaddr *)&address,sizeof(address));
return 0;
}
client端
```
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
char *ip = argv[1]; //用户自身的IP
int port = atoi(argv[2]); //和服务器端口对应及2000
int ret = 0;
int client_fd;
char buf[BUFFER_SIZE];
struct sockaddr_in address,addr;
socklen_t len = sizeof(addr);
if(argc <= 2)
{
printf("error\n");
}
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);
client_fd = socket(PF_INET,SOCK_DGRAM,0);
assert(client_fd >= 0);
ret = bind(client_fd,(struct sockaddr *)&address,sizeof(address));
assert(ret != -1);
printf("准备接收数据\n");
ret = recvfrom(client_fd,buf,BUFFER_SIZE-1,0,(struct sockaddr *)&addr,&len);
buf[ret] = '\0';
printf("\n%s\n",buf);
return 0;
}
UDP广播
广播的特点是:子网中未参加相应广播应用的所有主机都会受到广播影响
向一个广播地址发送UDP数据报时所发生的步骤为:
首先假设子网地址为192.168.34,则该子网的广播地址为192.168.34.255,此子网下有俩台IP地址分别为192.168.34.1和192.168.34.2的主机
(1)广播时sendto向IP为192.168.34.255端口为3333发送数据报,此IP数据报在以太网下会激活ARP将目的广播地址192.168.34.255映射成相应的以太网地址(物理地址),然后将目的地址为此地址的帧发出去
(2)这次IP为192.168.34.1和192.168.34.2的主机接口都会和此广播地址匹配成功,直到该数据报进入UDP层时根据端口是否匹配才判断是否丢弃该数据,端口为3333的会保留该数据,否则将数据丢弃
**总结**
广播:根据定义,广播分组会去往子网上的所有主机,包括发送主机自身,广播的缺点:未参加广播的主机也会受到影响
(3)广播实例代码
server端
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#define BUFFER_SIZE 1024
int main()
{
char *ip="192.168.34.255"; //广播地址
char *port1 = "3333"; //广播的端口号
int on=1;
int port = atoi(port1);
int ret = 0;
int udpfd;
char buf[BUFFER_SIZE]={
"hello,world"
};
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);
udpfd = socket(PF_INET,SOCK_DGRAM,0);
assert(udpfd >= 0);
//将udpfd的属性设置为广播
if((setsockopt(udpfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on))) == -1)
{
printf("error\n");
}
sendto(udpfd,buf,BUFFER_SIZE-1,0,(struct sockaddr *)&address,sizeof(address));
return 0;
}
client端
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
char *ip = argv[1]; //IP地址自己主机的任意接口IP
int port = atoi(argv[2]); //端口号与服务器一致及3333
int ret = 0;
int client_fd;
char buf[BUFFER_SIZE];
struct sockaddr_in address,addr;
socklen_t len = sizeof(addr);
if(argc <= 2)
{
printf("error\n");
}
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);
client_fd = socket(PF_INET,SOCK_DGRAM,0);
assert(client_fd >= 0);
ret = bind(client_fd,(struct sockaddr *)&address,sizeof(address));
assert(ret != -1);
printf("准备接收数据\n");
ret = recvfrom(client_fd,buf,BUFFER_SIZE-1,0,(struct sockaddr *)&addr,&len);
buf[ret] = '\0';
printf("\n%s\n",buf);
return 0;
}