9.1 UDP协议
TCP 协议是面向连接的基于流的,可靠的传输服务。UDP是无连接的,基于数据报的,不可靠的传输服务,UDP没有粘包,但是会产生丢包。
UDP模型如下:
可以看到,服务器端不用listen,也不用accept。而客户端,也不用connect。
总结UDP的特点如下:
1、无连接
2、基于消息的数据传输服务
3、不可靠
4、一般情况下UDP更加高效
注意点:
1、UDP报文可能会丢失重复
2、UDP报文可能会乱序
3、UDP缺乏流量控制
4、UDP缓冲区写满后,没有流量控制机制,会覆盖缓冲区
5、UDP协议数据报文截断
如果接收到的数据报,大于缓冲区,报文可以被截断,后面的部分会丢失
6、recvfrom返回0,不代表连接关闭,因为UDP是无连接的
例如:sendto可以发送数据0包,只包含UDP头部,这时候recvfrem就会返回0
7、ICMP异步错误
观察现象:
关闭UDP服务端,如启动UDP客户端,从键盘接收数据后,再发送数据。UDP客户端会阻塞在recvfrom位置(因为没有对端给本机发),sendto是将数据写到
套接字缓冲区,UDP协议栈会选择时机发送。
说明:
1、UDP发送报文时,只把数据copy到数据缓冲区,在服务器没有起来的情况下可以发送成功。
2、所谓ICMP异步错误是指:发送报文的时候,没有错误,recvfrom接收报文的时候,会收到ICMP应答。
3、异步错误,是无法返回未连接的套接字,UDP也可以调用connect。
8、UDP connect
UDP调用connect,并没有三次握手,只是维护了一个状态信息(和对等方的)
一旦调用connect,就可以使用send函数
简单的UDP回射服务器程序如下:
服务器:
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- void echo_srv(int sock)
- {
- char recvbuf[] = {};
- struct sockaddr_in peeraddr;
- socklen_t peerlen;
- int n;
- while()
- {
- peerlen = sizeof(peeraddr);
- memset(recvbuf, , sizeof(recvbuf));
- n = recvfrom(sock, recvbuf, sizeof(recvbuf), , (struct sockaddr*)&peeraddr,
- &peerlen);
- if(n == -)
- {
- if(errno == EINTR)
- continue;
- else
- {
- perror("recvfrom error");
- exit();
- }
- }
- else if(n > )
- {
- int ret = ;
- fputs(recvbuf, stdout);
- ret = sendto(sock, recvbuf, n, , (struct sockaddr*)&peeraddr, peerlen);
- }
- }
- close(sock);
- }
- int main()
- {
- int sock;
- sock = socket(AF_INET, SOCK_DGRAM, );
- if(sock < )
- {
- perror("socket error");
- exit();
- }
- struct sockaddr_in servaddr;
- memset(&servaddr, , sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons();
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- if(bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
- {
- perror("bind error");
- exit();
- }
- echo_srv(sock);
- return ;
- }
客户端:
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- void echo_cli(int sock)
- {
- struct sockaddr_in servaddr;
- memset(&servaddr, , sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons();
- servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- int ret = ;
- char sendbuf[] = {};
- char recvbuf[] = {};
- while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
- {
- sendto(sock, sendbuf, strlen(sendbuf), , (struct sockaddr*)&servaddr,
- sizeof(servaddr)
- );
- ret = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
- if(ret == -)
- {
- if(errno == EINTR)
- continue;
- else
- {
- perror("recvfrom error");
- exit();
- }
- }
- fputs(recvbuf, stdout);
- memset(sendbuf, , sizeof(sendbuf));
- memset(recvbuf, , sizeof(recvbuf));
- }
- close(sock);
- }
- int main()
- {
- int sock;
- sock = socket(AF_INET, SOCK_DGRAM, );
- if(sock < )
- {
- perror("socket error");
- exit();
- }
- echo_cli(sock);
- return ;
- }
运行结果如下:
用netstat - na看网络状态如下:
UDP和TCP不一样,不存在11种状态,因此,我们只能看到一个服务器端的套接字,服务器端执行了bind,所以会显示这个套接字。
报文截断:
如果接收到的数据报,大于缓冲区,报文可以被截断,后面的部分会丢失
实验程序如下:
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #define ERR_EXIT(m) \
- do \
- { \
- perror(m); \
- exit(EXIT_FAILURE); \
- } while()
- int main(void)
- {
- int sock;
- if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
- ERR_EXIT("socket");
- struct sockaddr_in servaddr;
- memset(&servaddr, , sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons();
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
- ERR_EXIT("bind");
- sendto(sock, "ABCD", , , (struct sockaddr*)&servaddr, sizeof(servaddr));
- //数据报方式。。。。不是字节流
- //如果接受数据时,指定的缓冲区的大小,较小;
- //剩余部分将要截断,扔掉
- char recvbuf[];
- int n;
- int i;
- for (i=; i<; i++)
- {
- n = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
- if (n == -)
- {
- if (errno == EINTR)
- continue;
- ERR_EXIT("recvfrom");
- }
- else if(n > )
- printf("n=%d %c\n", n, recvbuf[]);
- }
- return ;
- }
这是一个自己发自己收的UDP程序,第38行我们定义的缓冲区为1字节大小,而33行发送的大小是4字节,recvfrom接收时会一次取出4字节,但是只放一字节到recvbuf中,其他的三字节被丢弃。 我们想看到的现象是recvfrem一个字节一个字节的接收,但是UDP是数据报协议,recvfrom一次接收一个数据报。跟TCP不一样。
下面做一个只启动客户端,不启动服务器的实验,程序如下:
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- void echo_cli(int sock)
- {
- struct sockaddr_in servaddr;
- memset(&servaddr, , sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons();
- servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- int ret = ;
- char sendbuf[] = {};
- char recvbuf[] = {};
- while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
- {
- ret = sendto(sock, sendbuf, strlen(sendbuf), , (struct sockaddr*)&servaddr,
- sizeof(servaddr)
- );
- printf("sendto %d bytes\n", ret);
- printf("send success\n");
- ret = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
- if(ret == -)
- {
- if(errno == EINTR)
- continue;
- else
- {
- perror("recvfrom error");
- exit();
- }
- }
- fputs(recvbuf, stdout);
- memset(sendbuf, , sizeof(sendbuf));
- memset(recvbuf, , sizeof(recvbuf));
- }
- close(sock);
- }
- int main()
- {
- int sock;
- sock = socket(AF_INET, SOCK_DGRAM, );
- if(sock < )
- {
- perror("socket error");
- exit();
- }
- echo_cli(sock);
- return ;
- }
只启动客户端运行,结果如下:
UDP也可以调用connect,但是并没有三次握手,只是维护了一个状态信息(和对等方的),实验程序如下:
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #define ERR_EXIT(m) \
- do \
- { \
- perror(m); \
- exit(EXIT_FAILURE); \
- } while()
- void echo_cli(int sock)
- {
- struct sockaddr_in servaddr;
- memset(&servaddr, , sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons();
- servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- //3 udp 也可以 调用connet
- //udp调用connet,并没有三次握手,只是维护了一个状态信息(和对等方的)。。。
- connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr));
- int ret;
- char sendbuf[] = {};
- char recvbuf[] = {};
- while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
- {
- //2如果 connect 已经指定了对方的地址。
- //send可以这样写 sendto(sock, sendbuf, strlen(sendbuf), 0, NULL, 0);
- //1sendto第一次发送的时候,会绑定地址
- sendto(sock, sendbuf, strlen(sendbuf), , (struct sockaddr*)&servaddr, sizeof(servaddr));
- /*sendto(sock, sendbuf, strlen(sendbuf), 0, NULL, 0);*/
- //一但调用connect,就可以使用send函数
- //send(sock, sendbuf, strlen(sendbuf), 0);
- ret = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
- if (ret == -)
- {
- if (errno == EINTR)
- continue;
- ERR_EXIT("recvfrom");
- }
- fputs(recvbuf, stdout);
- memset(sendbuf, , sizeof(sendbuf));
- memset(recvbuf, , sizeof(recvbuf));
- }
- close(sock);
- }
- int main(void)
- {
- int sock;
- if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
- ERR_EXIT("socket");
- echo_cli(sock);
- return ;
- }
一旦调用了connect,就可以使用send函数发送数据了,不在必须使用sendto。使用send发送数据时,目标地址是connect中绑定的地址。只启动客户端,执行结果如下:
上述程序我们只是在28行加上了connect,如果不加这个函数,客户端会阻塞在recvfrem处。调用了connect后,情况就有点不一样了,sendto还是正常发送数据,但是执行到recvfrom处接收到了ICMP报文,UDP接收到这个异常报文后,给recvfrom返回错误,显示连接拒绝,直接退出客户端。
9.1 UDP协议的更多相关文章
- TODO:Golang语言TCP/UDP协议重用地址端口
TODO:Golang语言TCP/UDP协议重用地址端口 这是一个简单的包来解决重用地址的问题. go net包(据我所知)不允许设置套接字选项. 这在尝试进行TCP NAT时尤其成问题,其需要在同一 ...
- 闲来无事,写个基于UDP协议的Socket通讯Demo
项目一期已经做完,二期需求还没定稿,所以最近比较闲. 上一篇写的是TCP协议,今天写一下UDP协议.TCP是有连接协议,所以发送和接收消息前客户端和服务端需要建立连接:UDP是无连接协议,所以发送消息 ...
- UDP协议开发
UDP是用户数据报协议(User Datagram Protocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务.与TCP协议不同,UDP协议直接利用I ...
- 基于UDP协议模拟的一个TCP协议传输系统
TCP协议以可靠性出名,这其中包括三次握手建立连接,流控制和拥塞控制等技术.详细介绍如下: 1. TCP协议将需要发送的数据分割成数据块.数据块大小是通过MSS(maximum segment siz ...
- TCP协议与UDP协议的区别
TCP协议与UDP协议的区别(转) 首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! ...
- 采用UDP协议的PIC32MZ ethernet bootloader
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 经过千辛万苦,今天终于 ...
- 采用UDP协议实现PIC18F97J60 ethernet bootloader
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). TCP/IP Stac ...
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7549340 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...
随机推荐
- robot_pose的类型
http://docs.ros.org/api/geometry_msgs/html/msg/Pose.html
- Codeforces 496D - Tennis Game
496D - Tennis Game 思路:枚举每个t,求出对应的满足条件的s. 代码: #include<bits/stdc++.h> using namespace std; #def ...
- sgu 116 Index of super-prime
题意:用最少的super-prime组成n; 找出所有的super-prime数,只有202个.用完全背包记录能取到n值的最少数量.再找出7要哪些元素. #include <iostream&g ...
- Oracle11g温习-第六章:控制文件
2013年4月27日 星期六 10:33 .控制文件的功能和特点 1) [定义数据库当前物理状态] 2) [维护数据的一致性] 如果控制文件中的检查点与数据文件中的一致,则说明数据一致,可以启动到 ...
- Oracle 11g 物理Dataguard日常操作维护(二)
Oracle 11g 物理Dataguard日常操作维护(二) 2017年8月25日 14:34 3.3 3.3.1 查看备库进程状态 SYS(125_7)@fpyj123> select pr ...
- python读写csv时中文乱码问题解决办法
https://www.cnblogs.com/shengulong/p/7097869.html 参考1 参考2 参考3 CSV是英文Comma Separate Values(逗号分隔值)的缩写, ...
- SQL基础分页存储过程(案例一)
--分页 存储过程 案例 -- 所执行的存储过程 create proc pageForUsers @currPage int, --当前页数 @pageSize int, --每页多少条记录 @co ...
- kill word out 1
1● de 使~~ 成为 :离开 ,去掉,向下,变慢 2● dif 不,分开 ,否定,离开 3● deci 十分之一 4● deca 向下,离开 5● deca 十 6● di ...
- VC++ 报错:Heap corruption detected
今天在写代码时,发现莫名其妙的错误: std::string strName = L“testtest”; char* pOutString = new char(len + 1); Decrypt( ...
- cas AuthenticationFilter
AuthenticationFilter *** 这个类的作用:判断是否已经登录,如果没有登录则根据配置的信息来决定将跳转到什么地方 *** casServerLoginUrl:定义cas 服务器的登 ...