文章目录
一、UDP协议
简介
UDP全称是用户数据报协议,是一种无连接的传输协议。相比于TCP它的优缺点:
优点:传输速度快、资源消耗小、编程简单,在音视频数据传输中常用。
缺点:网络质量不好时,丢包严重、会照成数据丢失、损毁。
通信流程
因为UDP是无连接的,所以通信流程会略有不同:
从图可以看出,使用UDP进行通信不需要双方建立连接就可以直接进行通信。
二、API函数
使用UDP进行网络编程,Linux另外提供了一些函数。
2.1 sendto发送
这个是在UDP模式下使用的数据发送函数,与TCP模式的区别就是每次发送都需要目标的地址等信息。
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数说明:
- sockfd:socket文件描述符。
- buf:发送的内容。
- len:发送数据的长度。
- flags:标志,可以使用此参数设置不同数据传输方式,一般使用0。
- dest_addr:目标地址信息,封装在socketaddr结构体中。
- addrlen:目标地址信息大小。
- 返回值:发送成功返回发送的字节数,失败返回-1,并设置相应的错误到errno。
2.2 recvfrom接收
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数说明:
- sockfd:socket文件描述符。
- buf:数据缓冲区,用于存放接收的数据。
- len:需要接收的数据长度。
- flags:标志,可以使用此参数设置不同数据传输方式,一般使用0。
- src_addr:源地址信息,封装在socketaddr结构体中。
- addrlen:源地址信息大小,指针。
- 返回值:接收成功返回接收到的字节数,失败返回-1,并设置相应的错误到errno。
其他函数参看前面的博客:
三、UDP通信实例
client.c(先发送端):
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef struct sockaddr* saddrp;
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (0 > sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(12345);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t addr_len = sizeof(struct sockaddr_in);
while(1)
{
char buf[255] = {};
printf("Please input cData:");
gets(buf);
sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&addr,sizeof(addr));
if(0 == strncmp(buf, "end", 3)) break;
recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&addr,&addr_len);
printf("cRecv:%s\n",buf);
printf("the ipaddr = %#x\n", addr.sin_addr.s_addr);
printf("the port = %d\n", addr.sin_port);
if(0 == strncmp(buf, "end", 3)) break;
}
close(sockfd);
return 0;
}
server.c(先接收端):
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
typedef struct sockaddr* saddrp;
int main(int argc, char const *argv[])
{
//创建socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (0 > sockfd)
{
perror("sockfd");
return -1;
}
//准备地址
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;//ipv4
addr.sin_port = htons(12345);//端口号
addr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取ip
//绑定
int ret = bind(sockfd,(saddrp)&addr,sizeof(addr));
if (0 > ret)
{
perror("bind");
return -1;
}
struct sockaddr_in src_addr ={};
socklen_t addr_len = sizeof(struct sockaddr_in);
while(1)
{
char buf[255] = {};
//接收数据和来源的ip地址
recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&src_addr,&addr_len);
printf("sRecv:%s\n",buf);
if (0 == strncmp(buf,"end",3)) break;
//发送数据给目标地址
printf("Please input sData to send:");
gets(buf);
sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&src_addr,addr_len);
if (0 == strncmp(buf,"end", 3)) break;
}
//关闭socket对象
close(sockfd);
return 0;
}
运行结果:
client:
server:
分析:
客户端先发送数据给服务器,服务区获取到数据后知道客户端的地址信息,就可以向客户端发送数据了,一般都是客户端先发送,服务器先接收。输入end会两边都会判断进行结束。