udp是User Datagram Protocol的简称, 中文名是用户数据报协议。udp协议位于osi模型中的传输层,它是一种面向无连接的协议。udp协议并不保证数据一定能够到达对端,也不能保证数据能够按照顺序到达对端,udp数据的可靠性需要引用层来保证。
udp提供的虽然是不可靠的数据传输,但是udp在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序。
1)socket建立套接字,需要指定ip类型和协议类型
socket(AF_INET,SOCK_DGRAM,0);
2)bind绑定服务器ip和端口
bind(sockfd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
3)recvfrom接收来自客户端的数据
4)sento发送数据到客户端
1)socket建立套接字,需要指定ip类型和协议类型
2)recvfrom接收来自客户端的数据
3)sento发送数据到客户端
协议 | UDP | TCP |
---|---|---|
是否面向连接 | 否 | 是 |
传输可靠性 | 不可靠 | 可靠 |
传输速度 | 慢 | 快 |
应用场合 | 数据量少,对可靠性要求不高 | 数据量大,对可靠性要求较高 |
当我们需要拨打电话给某个人的时候,需要知道对方的电话号码,并且只有在对方接通电话之后才能进行正常通话,tcp面向连接就类似于拨打电话,客户端需要知道服务器的ip地址和端口并且建立连接之后才能进行正常通讯。
udp通信方式类似于对讲机,只要对方的对讲机打开,我们就可以进行语音通信,并不需要对方进行接听动作,这就是无连接。通常一个人对着对讲机说话,使用同一频段的对讲机都可以听到这个人的声音,这点也和udp广播和组播类似。
在tcp中,服务器必须开启之后,客户端才能连接上服务器,并在建立连接之后进行数据通信;而对于udp,不管服务器是否开启,udp客户端都可以正常创建并发送数据,只不过可能没有服务器接收你的数据而已。
tcp协议提供非常可靠的数据传输方式,tcp在进行数据通信前会进行三次握手,保证通信双方能够接收对方的消息并且正确解析;在传输过程中tcp提供了重传功能,能够保证消息完整有序的到达接收端;在连接断开的时候,tcp能够通过四次握手保证最后一帧数据的完整性;
udp通信既没有三次握手和四次挥手,也没有重传机制,它只保证数据正确的发送出去,并不关心对端能不能收到,也不关心对端的数据是否接收完整或者是否按照顺序到达。
tcp由于其高可靠性,多应用于数据量大并且对可靠性要求高的场合,比如http协议;udp虽然不可靠,但是传输速度很快,效率很高,而且客户端和服务器端并不相互依赖,比较适用于对及时性要求高的场合,比如早期的qq就是使用的udp协议。
我们通过客户端发送Test udp message!字符串到8099端口,使用wireshark抓取数据包如下:
可以看出udp协议的格式非常简单,它只有8个字节的首部,后面的全是数据。
目标:在linux下建立一个客户端和一个服务器,通过客户端发送字符串“Test udp message!”,服务器收到之后回复“Got it!”。
服务器端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define UDP_PORT 8099
int main()
{
int ret;
int read_len = 0;
int thread_fd;
int sockfd;
int server_fd;
struct sockaddr_in server_addr;
struct sockaddr client_addr;
socklen_t len;
char buffer[1024];
server_addr.sin_family = AF_INET; //使用IPV4
server_addr.sin_port = htons(UDP_PORT);//服务器端口号
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//主机可能有多个网卡,INADDR_ANY表示绑定所有网卡
sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建TCP,指定ip类型为IPV4
if(sockfd < 0)
{
printf("socket create fail!\r\n");
return -1;
}
ret = bind(sockfd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));//将socket和服务器地址绑定
if(ret < 0)
{
printf("socket bind fail!\r\n");
}
while(1)
{
memset(buffer,0,sizeof(buffer));
read_len = recvfrom(sockfd,buffer,sizeof(buffer),0,&client_addr,&len);
printf("recv(%d):%s\r\n",read_len,buffer);//最后面如果不带\r\n可能会导致在shell中不显示数据
if(read_len > 0)
{
sendto(sockfd,"Got it!",strlen("Got it!"),0,&client_addr,sizeof(client_addr));
}
}
close(sockfd);
return 0;
}
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEST_ADDR "192.168.3.15"
#define UDP_PORT 8099
int main()
{
int ret;
int read_len = 0;
int sockfd;
int server_fd;
struct sockaddr_in dest_addr;
struct sockaddr src_addr;
socklen_t len;
dest_addr.sin_family = AF_INET; //使用IPV4
dest_addr.sin_port = htons(UDP_PORT);//服务器端口号
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);//主机可能有多个网卡,INADDR_ANY表示绑定所有网卡,适用于本地通信
//dest_addr.sin_addr.s_addr = inet_addr(DEST_ADDR);//适用于局域网通信
sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建TCP,指定ip类型为IPV4
if(sockfd < 0)
{
printf("socket create fail!\r\n");
return -1;
}
while(1)
{
char buffer[1024] = "Test udp message!";
ret = sendto(sockfd,buffer,strlen(buffer),0,&dest_addr,sizeof(dest_addr));
if(ret < 0)
{
printf("sendto error\r\n");
continue;
}
memset(buffer,0,sizeof(buffer));
read_len = recvfrom(sockfd,buffer,sizeof(buffer),0,&src_addr,&len);
printf("recv:%s\r\n",buffer);
sleep(1);
}
close(sockfd);
return 0;
}