1. Introduction
User Datagram Protocol is an unreliable and connectionless protocol. Before data is sent, it does not need to establish a connection in advance, which can transmit data more efficiently (send emails)
2. Scenarios used
(1) Send small size data
(2) In a network where it is difficult to receive data and give a response
(3) Broadcast/multicast communication
(4) QQ/WeChat and other instant communication software point-to-point file communication and audio and video calls
(5) Live room
2. API interface
1. socket interface
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 (binding address)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:待连接套接字
addr:包含本地地址(IP+PORT)的通用地址结构体的指针
addrlen:地址结构体大小
返回值:
成功:0
失败:-1
3. Address structure
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 (send data to 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 (receive data from 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:地址长度
返回值:
成功:已接收字节数
3. The process of UDP creation
1. Server (receiving end)
(1) Create a mailbox (socket)
(2) Set the address
(3) Bind the address with the mind (bind)
(4) Waiting for a letter (recvfrom)
2. Client (sender)
(1) Create a mailbox (socket)
(2) Set the sending address
(3) Write the content of the letter
(4) Send a letter (the content is sent together with the address information)
4. Code
1. Server (receiving end)
#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. Client (sender)
#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;
}