目录
UDP介绍
UDP编程
函数接口
recvfrom
sendto
小练习:实现服务器和客户端相连(使用UDP实现)
client
server
UDP介绍
UDP(User Datagram Protocol,用户数据报协议)是一种在计算机网络中常用的传输层协议。与TCP(Transmission Control Protocol,传输控制协议)相比,UDP是一种无连接的协议,它提供了一种简单的、无状态的数据传输方式。
下面是UDP的一些关键特点和用途:
-
面向无连接:UDP在通信之前不需要建立连接,发送方直接将数据报发送到目标地址。这种无连接的特性使UDP的通信过程更加简洁高效,但也导致了它的不可靠性。
-
不可靠性:由于UDP缺乏数据确认、重传机制和流量控制,因此它无法保证数据的可靠性和顺序。数据报可能会丢失、重复、乱序或损坏。这使得UDP在一些对数据完整性要求较低、实时性较高的应用中更为适用,如音频、视频、实时游戏等。
-
低延迟:由于UDP的简单性和无连接性,它的处理延迟较低,适用于那些对实时性要求较高的应用场景。例如,音频和视频流媒体通常使用UDP来避免因TCP的拥塞控制机制而引入的较大延迟。
-
支持广播和多播:UDP可以向一个特定的IP广播地址或多播地址发送数据报,以实现将数据传输给多个接收方的应用场景,如视频会议、实时流广播等。
-
轻量级:相对于TCP,UDP的头部开销较小,传输的数据报包含更少的控制信息。这使得UDP在网络带宽较低或资源有限的环境中更加高效。
UDP被广泛用于许多网络应用中,例如音频和视频流媒体(如VoIP、视频聊天)、实时游戏、DNS(Domain Name System,域名系统)等。尽管UDP具有不可靠性和丢包的风险,但通过适当的应用层协议和机制,可以弥补这些缺点,使UDP在特定的应用场景中发挥重要作用。
UDP编程
注:上图主要是一种思想,实际编程思路如下
udb流程 (类似短信)
server:
创建数据报套接字 sockt (SOCK_DGRAM)
绑定 bind 绑定
接收 recvfrom 接收信息
关闭套接字 close
client:
创建数据包套接字 sockt (SOCK_DGRAM)
发送 sendto
关闭套接字 close
函数接口
recvfrom
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:发送端的网络信息结构体的大小的指针
返回值:
成功接收的字节个数
失败:-1
0:客户端退出
sendto
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
src_addr:接收端的网络信息结构体的指针
addrlen:接收端的网络信息结构体的大小
返回值:
成功发送的字节个数
失败:-1
小练习:实现服务器和客户端相连(使用UDP实现)
client
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define Port 2065
#define N 128
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
struct sockaddr_in saddr, caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(Port);
saddr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(caddr);
if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
{
perror("bind err.");
return -1;
}
char buf[N];
char q[N];
//注意这里绑定得是发送段得结构体
//注意循环位置
while (1)
{
ssize_t recvid = recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&caddr, &len); //这里得0不代表阻塞得意思
if (recvid < 0)
{
perror("recvfrom err.");
return -1;
}
printf("端口:%d\n", ntohs(caddr.sin_port)); //注意这里得转换函数
printf("IP地址:%s\n", inet_ntoa(caddr.sin_addr)); //将网络字节序得二进制转换为字符型
printf("%s\n", buf);
sprintf(q, "recv:%s", buf);
sendto(sockfd, q, N, 0, (struct sockaddr *)&caddr, len);
}
close(sockfd);
return 0;
}
server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define Port 2065
#define N 128
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(Port);
saddr.sin_addr.s_addr = INADDR_ANY;
char buf[N];
socklen_t len = sizeof(saddr);
while (1)
{
fgets(buf, N, stdin);
if (buf[strlen(buf) - 1] == '\n')
{
buf[strlen(buf) - 1] = '\0';
}
//绑定接受段得结构体
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, sizeof(saddr));
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len);
printf("%s\n",buf);
}
close(sockfd);
return 0;
}