一、简介
用户数据报协议,是一种不可靠无连接的协议,在数据发送前,不需要提前建立连接,可以更高效传输数据(发邮件)
2、使用的场景
(1)发送小尺寸数据
(2)在收到数据,给出应答比较困难的网络中
(3)广播/组播式通信
(4)QQ/微信等及时通信软件地点对点文件通讯以及音视频通话
(5)直播间
二、API接口
1、socket接口
int socket(int domain, int type, int protocol);
参数:
domain:域。
AF_INET/PF_INET: 网际协议
AF_UNIX/PF_UNIX:本地协议,可写成 AF_LOCAL/PF_LOCAL
type:类型。
SOCK_STREAM:流式套接字 TCP协议
SOCK_DGRAM:数据报套接字 UDP协议
protocol:协议。
一般为 0
返回值:
成功:待连接套接字
失败:-1
2、bind(绑定地址)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:待连接套接字
addr:包含本地地址(IP+PORT)的通用地址结构体的指针
addrlen:地址结构体大小
返回值:
成功:0
失败:-1
3、地址结构体
struct sockaddr // 通用IP信息结构体
{
sa_family_t sa_family;
char sa_data[14];
}
struct sockaddr_in // IPV4地址结构体
{
u_short sin_family;// 地址族
u_short sin_port;// 端口
struct in_addr sin_addr;// IPV4 地址
char sin_zero[8];
};
struct in_addr // IP地址结构体
{
in_addr_t s_addr;// 无符号 32 位网络地址
};
4、sendto( 发送数据到UDP)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
sockfd:UDP 套接字
buf:即将发送的数据
len:数据的长度
flags:发送标志,与函数 send 的 flags 完全一致
dest_addr:对端网络地址
addr_len:地址长度
返回值:
成功:已发送字节数
失败:-1
5、recvfrom(从UDP中接收数据)
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数:
sockfd:UDP 套接字
buf:储存数据缓冲区
len:缓冲区大小
flags:接收标志,与函数 send 的 flags 完全一致
src_addr:对端网络地址
addrlen:地址长度
返回值:
成功:已接收字节数
三、UDP创建的流程
1、服务器(接收端)
(1)创建邮箱(socket)
(2)设置好地址
(3)将地址与心想绑定(bind)
(4)坐等来信(recvfrom)
2、客户端(发送端)
(1)创建一个邮箱(socket)
(2)设置发送的地址
(3)编写信件内容
(4)发送信件(内容与地址信息一起发送)
四、代码
1、服务器(接收端)
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h> // 包含了地址结构体的定义声明
#include <netinet/in.h>
int main(int argc, char const *argv[])
{
// 创建一个邮箱
int socket_fd = socket(AF_INET, SOCK_DGRAM , 0 );
if (-1 == socket_fd)
{
perror("socket error");
return -1 ;
}
// 设置好IP+端口号等信息
// struct sockaddr_in // IPV4地址结构体
// {
// u_short sin_family;// 地址族
// u_short sin_port;// 端口
// struct in_addr sin_addr;// IPV4 地址
// char sin_zero[8];
// };
struct sockaddr_in my_addr = {0} ;
int addr_len = sizeof(struct sockaddr_in);
my_addr.sin_family = AF_INET ; // 网际协议 IPV4
my_addr.sin_port = htons(65000); // 设置端口号为65000 并转换为网络字节序,范围:0~65535
my_addr.sin_addr.s_addr = inet_addr("192.168.102.2"); // 设置IP地址, 并转换为32位的网络地址
//在终端ifconfig取地址
// 把设置好的信息与信箱进行帮定
int ret_val = bind(socket_fd , (struct sockaddr *)&my_addr,addr_len);
if (-1 == ret_val)
{
perror("bind error");
return -1 ;
}
char * msg = calloc(128,1);//char型的指针
struct sockaddr_in from_addr ;
// 坐等来信
while(1)
{
// ret_val = read(socket_fd , msg , 128); // 可以直接使用读取函数来读取数据,但是没办法得知数据来自哪里
ret_val = recvfrom(socket_fd , msg , 128 , 0 , (struct sockaddr *)&from_addr, &addr_len );
if (ret_val == -1 )
{
perror("recv error");
continue ;
}
else{
printf("recv succeed \n addr:%s \nport:%d \nmsg : %s \n" ,
inet_ntoa( from_addr.sin_addr ) , // 把二进制的网络字节序的IP地址转换点分十进制的字符串地址
ntohs(from_addr.sin_port) , // 把网络字节序的端口号转换位 本地字节序的短整型
msg );
}
}
return 0;
}
2、客户端(发送端)
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h> // 包含了地址结构体的定义声明
#include <netinet/in.h>
int main(int argc, char const *argv[])
{
// 创建一个邮箱
int socket_fd = socket(AF_INET, SOCK_DGRAM , 0 );
if (-1 == socket_fd)
{
perror("socket error");
return -1 ;
}
// 设置好IP+端口号等信息
// struct sockaddr_in // IPV4地址结构体
// {
// u_short sin_family;// 地址族
// u_short sin_port;// 端口
// struct in_addr sin_addr;// IPV4 地址
// char sin_zero[8];
// };
struct sockaddr_in dest_addr = {0} ;
int addr_len = sizeof(struct sockaddr_in);
dest_addr.sin_family = AF_INET ; // 网际协议 IPV4
dest_addr.sin_port = htons(65000); // 设置端口号为65000 并转换为网络字节序
dest_addr.sin_addr.s_addr = inet_addr("192.168.102.2"); // 设置IP地址, 并转换为32位的网络地址
char * msg = calloc(128,1);
struct sockaddr_in from_addr ;
int ret_val = -1 ;
// 坐等来信
while(1)
{
printf("请输入需要发送的消息:\n");
fgets(msg , 128 , stdin) ;//stdin指的是从哪里输入
ret_val = sendto(socket_fd , msg , strlen(msg) , 0 ,
(struct sockaddr * )&dest_addr, addr_len );
if ( -1 == ret_val )
{
perror("send to error ");
continue ;
}
else{
printf("send succeed %d byte ! \n" , ret_val);
}
}
return 0;
}