UDP(用户数据报协议)是一种无连接的网络协议,主要用于快速传输数据。以下是UDP协议的一些主要特点:
-
**无连接**:UDP是无连接的协议,这意味着在数据传输之前不需要建立连接。每个UDP数据包都是独立的,不需要前一个数据包的确认。
-
**简单性**:UDP的协议结构相对简单,只有8字节的UDP头部,包括源端口、目的端口、长度和校验和。
-
**快速性**:由于UDP不需要建立连接和进行数据确认,因此它的数据传输速度通常比TCP快。
-
**不保证可靠性**:UDP不保证数据的可靠传输,也就是说,数据包可能会丢失、重复或乱序到达。
-
**无拥塞控制**:UDP没有拥塞控制机制,这意味着它不会根据网络的拥堵情况调整数据传输速率。
-
**支持多播和广播**:UDP支持多播和广播数据传输,这使得它可以用于发送数据到多个接收者。
-
**校验和**:UDP包含一个校验和字段,用于检测数据在传输过程中是否出现错误。如果检测到错误,接收方可以选择丢弃该数据包。
-
**应用场景**:UDP通常用于那些对实时性要求高但可以容忍一定数据丢失的应用,如视频会议、在线游戏、DNS查询等。
-
**端口号**:UDP使用端口号来区分不同的服务或进程,每个UDP数据包都包含源端口和目的端口。
-
**数据报长度**:UDP数据报的长度可以是任意的,但最大长度受限于IP数据报的最大长度,通常是65507字节。
2、框架: C/S模式
服务器端 server:socket() ===>bind()===>recvfrom()===>close()
客户端 client:socket() ===>bind()===>sendto() ===>close()
注意:socket()的参数需要调整。
socket(PF_INET,SOCK_DGRAM,0);
bind() 客户端是可选的,服务器端是必须选的。
![](https://i-blog.csdnimg.cn/direct/7e532f42df14454e89eec037acc87e77.png)
1、int socket(int domain, int type, int protocol);
功能:程序向内核提出创建一个基于内存的套接字描述符
参数:domain 地址族,PF_INET == AF_INET ==>互联网程序
PF_UNIX == AF_UNIX ==>单机程序
type 套接字类型:
SOCK_STREAM 流式套接字 ===》TCP
SOCK_DGRAM 用户数据报套接字===>UDP
SOCK_RAW 原始套接字 ===》IP
protocol 协议 ==》0 表示自动适应应用层协议。
返回值:成功 返回申请的套接字id
失败 -1;
2、int bind(int sockfd, struct sockaddr *my_addr,
socklen_t addrlen);
功能:如果该函数在服务器端调用,则表示将参数1相关
的文件描述符文件与参数2 指定的接口地址关联,
用于从该接口接受数据。
如果该函数在客户端调用,则表示要将数据从
参数1所在的描述符中取出并从参数2所在的接口
设备上发送出去。
注意:如果是客户端,则该函数可以省略,由默认
接口发送数据。
参数:sockfd 之前通过socket函数创建的文件描述符,套接字id
my_addr 是物理接口的结构体指针。表示该接口的信息。
struct sockaddr 通用地址结构
{
u_short sa_family; 地址族
char sa_data[14]; 地址信息
};
转换成网络地址结构如下:
struct _sockaddr_in ///网络地址结构
{
u_short sin_family; 地址族
u_short sin_port; ///地址端口
struct in_addr sin_addr; ///地址IP
char sin_zero[8]; 占位
};
struct in_addr
{
in_addr_t s_addr;
}
socklen_t addrlen: 参数2 的长度。
返回值:成功 0
失败 -1;
发送接收函数:
3、ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:用于UDP协议中向对方发送数据。
参数:sockfd 本地的套接字id
buff 本地的数据存储,一般是要发送的数据。
len 要发送的数据长度
flags 要发送数据方式,0 表示阻塞发送。
dest_addr: 必选,表示要发送到的目标主机信息结构体。
addrlen :目标地址长度。
返回值:成功 发送的数据长度
失败 -1;
4、ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:用于UDP协议中获取对方发送的数据。
参数:sockfd 本地的套接字id
buff 要存储数据的内存区,一般是数组或者动态内存。
len 要获取的数据长度,一般是buff的大小。
flags 获取方式,0 阻塞
src_addr 可选,表示对方的地址信息结构体,
如果为NULL,表示不关心对方地址。
addrlen 对方地址信息结构体大小。
如果对方地址是NULL,则该值也为NULL。
返回值:成功 接收到的数据长度
失败 -1;
《1》实现udp通信示例代码:
服务器端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
// man 7 ip
struct sockaddr_in ser,cli;
bzero(&ser,sizeof(ser));
bzero(&cli,sizeof(cli));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("192.168.203.128");
int ret = bind(sockfd,(SA)&ser,sizeof(ser));
if(-1 == ret)
{
perror("bind");
exit(1);
}
socklen_t len = sizeof(cli);
while(1)
{
char buf[512]={0};
recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
time_t tm;
time(&tm);
sprintf(buf,"%s %s",buf,ctime(&tm));
sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
}
close(sockfd);
return 0;
}
客户端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("192.168.203.128");
while(1)
{
char buf[512]="hello,this is udp test";
sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));
bzero(buf,sizeof(buf));
recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
printf("buf is %s\n",buf);
sleep(1);
}
close(sockfd);
return 0;
}
《2》udp实现文件传输
服务器端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
// man 7 ip
struct sockaddr_in ser,cli;
bzero(&ser,sizeof(ser));
bzero(&cli,sizeof(cli));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
//ser.sin_addr.s_addr = inet_addr("127.0.0.1");
ser.sin_addr.s_addr = INADDR_ANY;
int ret = bind(sockfd,(SA)&ser,sizeof(ser));
if(-1 == ret)
{
perror("bind");
exit(1);
}
socklen_t len = sizeof(cli);
int fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
char buf[512]={0};
int rd_ret = recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
if(0 == strcmp(buf,"^_^"))
{
break;
}
write(fd,buf,rd_ret);
bzero(buf,sizeof(buf));
strcpy(buf,"go on");
sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
}
close(sockfd);
close(fd);
return 0;
}
客户端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("192.168.203.128");
int fd = open("/home/linux/1.png",O_RDONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
char buf[512]={0};
while(1)
{
bzero(buf,sizeof(buf));
int rd_ret = read(fd,buf,sizeof(buf));
if(0==rd_ret)
{
break;
}
sendto(sockfd,buf,rd_ret,0,(SA)&ser,sizeof(ser));
bzero(buf,sizeof(buf));
recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
}
bzero(buf,sizeof(buf));
strcpy(buf,"^_^");
sendto(sockfd,buf,3,0,(SA)&ser,sizeof(ser));
close(sockfd);
close(fd);
return 0;
}
《3》udp实现小聊天室功能
服务器端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct
{
TYPE type;
char name[50];
char context[128];
}MSG;
typedef struct
{
struct sockaddr_in cli;
int flag; // 0 free 1 occu
}LIST;
#define MAX 10
LIST list[MAX]={0};
int do_login(int sockfd,MSG* msg,struct sockaddr_in* cli)
{
int i = 0 ;
for(i=0;i<MAX;i++)
{
if(1 == list[i].flag )
{
sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
}
}
for(i=0;i<MAX;i++)
{
if(0 == list[i].flag )
{
list[i].flag =1;
//list[i].cli = *cli;
memcpy(&list[i].cli,cli,sizeof(*cli));
break;
}
}
return 0;
}
int do_chat(int sockfd, MSG* msg,struct sockaddr_in*cli)
{
int i = 0 ;
for(i=0;i<MAX;i++)
{
if(1 == list[i].flag && 0!=memcmp(&list[i].cli,cli,sizeof(*cli)) )
{
sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
}
}
}
int do_logout(int sockfd, MSG* msg,struct sockaddr_in*cli)
{
return 0;
}
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
// man 7 ip
struct sockaddr_in ser,cli;
bzero(&ser,sizeof(ser));
bzero(&cli,sizeof(cli));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret = bind(sockfd,(SA)&ser,sizeof(ser));
if(-1 == ret)
{
perror("bind");
exit(1);
}
socklen_t len = sizeof(cli);
MSG msg;
while(1)
{
bzero(&msg,sizeof(msg));
recvfrom(sockfd,&msg,sizeof(msg),0,(SA)&cli,&len);
switch(msg.type)
{
case CMD_LOGIN:
do_login(sockfd,&msg,&cli);
break;
case CMD_LOGOUT:
do_logout(sockfd,&msg,&cli);
break;
case CMD_CHAT:
do_chat(sockfd,&msg,&cli);
break;
}
}
close(sockfd);
return 0;
}
客户端:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct
{
TYPE type;
char name[50];
char context[128];
}MSG;
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
// man 7 ip
struct sockaddr_in ser,cli;
bzero(&ser,sizeof(ser));
ser.sin_family = AF_INET;
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof(cli);
MSG msg;
char name[50]={0};
printf("input name:");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]='\0';
msg.type = CMD_LOGIN;
strcpy(msg.name ,name);
strcpy(msg.context,"login");
sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));
pid_t pid = fork();
if(pid>0)
{
while(1)
{
bzero(&msg,sizeof(msg));
recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL);
printf("%s:%s\n",msg.name,msg.context);
}
}
else if(0==pid)
{
while(1)
{
printf("to all");
char buf[128]={0};
strcpy(msg.name,name);
msg.type = CMD_CHAT;
fgets(msg.context,sizeof(msg.context),stdin);//#quit
msg.context[strlen(msg.context)-1]='\0';
if(0==strcmp(msg.context,"#quit"))
{
msg.type = CMD_LOGOUT;
strcpy(msg.context,"CMD_LOGOUT");
}
sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));
}
}
else
{
perror("fork");
exit(1);
}
close(sockfd);
return 0;
}