TCP/UDP

目录

1、TCP和UDP区别

2、UDP包头

3、TCP包头

4、TCP三次握手

5、TCP四次挥手

6、具体代码:

7、TCP 编程流程

8、UDP 编程流程

9、WSASocket()和socket()函数区别

10、SOCK_DGRAM和SOCK_STREAM

11、send/sendto和recv/recvfrom区别

12、select

13、epoll

14、TCP的可靠和有序

14.1、超时重传

14.2、阻塞控制

14.3、流量控制(滑动窗口)

14.4、拥塞控制和流量控制区别

15、UDP如何实现可靠传输


 

1、TCP和UDP区别

原理区别:

  • TCP 是面向连接的,UDP 是面向无连接的(比如socket编程中TCP发送数据之前先要connect)
  • UDP 程序结构较简单
  • TCP 是面向字节流的,UDP 是基于数据报的
  • TCP 保证数据正确性,UDP 可能丢包
  • TCP 保证数据顺序,UDP 不保证
  • 对系统资源的要求(TCP较多,UDP少) 

注意:部分满足以下几点要求时,应该采用UDP 面向数据报方式:

  • 网络数据大多为短消息 
  • 拥有大量Client 
  • 对数据安全性无特殊要求 
  • 网络负担非常重,但对响应速度要求高 

具体编程时的区别 socket()的参数不同:

  • UDP Server不需要调用listen和accept 
  • UDP 收发数据用sendto/recvfrom函数 
  • TCP 地址信息在connect/accept时确定 
  • UDP 在sendto/recvfrom函数中每次均 需指定地址信息 
  • UDP shutdown函数无效

数据报是网络传输的数据的基本单元,包含一个报头和数据本身,其中报头描述了数据的目的地以及和其它数据之间的关系。同一报文的不同分组可以由不同的传输路径通过通信子网。UDP基于数据报
字节流方式指的是仅把传输中的报文看作是一个字节序列,在字节流服务中,由于没有报文边界,用户进程在某一时刻可以读或写任意数量的字节。 TCP基于字节流

2、UDP包头

3、TCP包头

4、TCP三次握手

TCP 的三次握手除了建立连接外,主要还是为了沟通 TCP 包的序号问题。A 告诉 B,我发起的包的序号是从哪个号开始的,B 同样也告诉 A,B 发起的 包的序号是从哪个号开始的。双方建立连接之后需要共同维护一个状态机,在建立连接的过程中,双方的状态变化时序图如下所示:

为什么连接建立需要三次握手,而不是两次握手?

防止失效的连接请求报文段被服务端接收,从而产生错误。PS:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。

若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

5、TCP四次挥手

A:B 啊,我不想玩了
B:哦,你不想玩了啊,我知道了
这个时候,只是 A 不想玩了,即不再发送数据,但是 B 可能还有未发送完的数据,所以需要等待 B 也主动关闭。
B:A 啊,好吧,我也不玩了,拜拜
A:好的,拜拜

这样整个连接就关闭了,当然上面只是正常的状态,也有些非正常的状态(比如 A 说完不玩了,直接跑路,B 发起的结束得不到 A 的回答,不知道该怎么办或则 B 直接跑路 A 不知道该怎么办),TCP 协议专门设计了几个状态来处理这些非正常状态:

为什么会有TIME_WAIT:客户端接收到服务器的FIN报文后进入TIME_WAIT状态而不是CLOSED,还需要等待2MSL,理由:

确保最后一个确认报文能够到达。如果server端没收到client端发来的确认报文,那么就会重新发送连接释放请求报文。保证让迟来的TCP报文段有足够的时间被识别和丢弃。连接结束了,网络中的延迟报文也应该被丢弃掉,以免影响立刻建立的新连接。

大量TIME_WAIT处理办法:改用长连接

为什么是四次挥手,不是三次?观察四次挥手过程可见报文段3包含了报文段2中的确认值,因此三次挥手只能将报文段3和报文段2合并。但这样合并是有问题的。被动关闭方发送报文段2只是确认主动关闭方发来的结束报文段,但并不代表自身的数据已经传输完毕。即就是当断开连接的时候,一个方向的断开,只是说明该方向数据已传输完毕,而另一方向或许还有数据,所以要等到另一个方向数据也全部传输完成后,才能实现三次握手。但是这个时间不确定,因此会造成主动关闭方的结束报文段长时间未得到响应而进行超时重传等等。造成了不必要的资源浪费甚至更意想不到的问题。

注意:这里在理解三次握手和四次挥手有一点很关键,即tcp传输是全双工,所以每次都要向对方确认并获取确认,在考虑为什么是三次握手四次挥手时,可以在每次通信中加入网络抖动,并分析如果产生网络抖动会有什么影响。

6、具体代码:

参考代码

7、TCP 编程流程

端口状态:

listen():

accept():

connect():

可以看到7688进程有两个socket管道、一个是1002本地监听、另一个是与外界通信socket。

1、这里先关闭客户端socket:

因为是客户端主动触发的关闭,所以服务端的状态变为CLOSE_WAIT状态,客户端状态变为FIN_WAIT_1->FIN_WAIT_2;这时不关闭服务端接受到的客户端的socket,等待2MSL后,就只剩服务端的监听端口了;

如果在2MSL之内,关闭服务端接受到的客户端的socket,则客户端socket进入TIME_WAIT状态:

2、反之先关闭服务端接收到的客户端socket,也是一样的效果;

3、这里直接先关闭服务端的监听socket:

所以任然可以用已建立连接的socket发消息;

8、UDP 编程流程

端口状态:

bind():

没有连接状态;

并且关闭客户端socket不会改变服务器进程的状态,只有当服务端的socket关闭时进程才会结束,并且是立即结束。

9、WSASocket()和socket()函数区别

WSASocket是Windows专用,支持异步操作;socket是unix标准,只能同步操作。

Socket可采用多线程实现非阻塞,winsock是socket的windows平台的实现。winsock是微软专门为windows操作系统开发的socket网络编程接口,而socket是通用网络编程接口。

socket() 函数创建一个通讯端点并返回一个套接口。但是在socket库中例程在应用于阻塞套接口时会阻塞。WSASocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。可是socket()却只能发过之后等待回消息才可做下一步操作!

10、SOCK_DGRAM和SOCK_STREAM

SOCK_STREAM 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。

SOCK_DGRAM 是无保障的面向消息的socket,主要用于在网络上发广播信息。

SOCK_STREAM 是基于TCP的,数据传输比较有保障。SOCK_DGRAM是基于UDP的,专门用于局域网,基于广播

SOCK_STREAM 是数据流,一般是tcp/ip协议的编程,SOCK_DGRAM分是数据报,是udp协议网络编程

11、send/sendto和recv/recvfrom区别

send(),recv()用于TCP,sendto()及recvfrom()用于UDP;

但是send(),recv()也可以用于UDP,sendto()及recvfrom()也可以用于TCP;

如果send(),recv()用于UDP,在send之前先要使用connect(客户端能发服务端能收、反之不行,why??,如果服务端使用recvfrom/sendto,客户端使用recv/send就可以正常收发);

int send(SOCKET s, const char FAR *buf, int len, int flags);
int recv(SOCKET s, char FAR * buf, int len, int flags);
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
int recvfrom(int sockfd, void *buf ,int len ,unsigned int lags ,struct sockaddr *from ,int *fromlen);

recvfrom注意第六个参数,是指针,意思是这个参数是会被修改的;

关于flags参数:

int send(int s, const void *msg, size_t len, int flags); 
flags取值有:
0: 与write()无异
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息

int recv(int s, void *buf, size_t len, int flags);
flags取值有:
0:常规操作,与read()相同
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息
MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失
MSG_WAITALL:通知内核直到读到请求的数据字节数时,才返回。

int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); 
flags取值有:
0:常规操作,与read()相同
MSG_OOB:指明发送的是带外信息
MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失

int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); 
flags取值有:
0: 与write()无异
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_OOB:指明发送的是带外信息

12、select

int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

maxfdp:需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查fd_set的所有1024位;

readfds:用来检查可读性的一组文件描述字,(数组)可读句柄,先从该参数中读到可读句柄,然后复制给第一个元素(windows上测试结果);

writefds:同上,用来检查可写性的一组文件描述字;

errorfds:同上,用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内);

timeout:用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 
有三种可能:

  • timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
  • timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
  • timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)

fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:

FD_ZERO(fd_set *fdset):将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。

FD_SET(fd_set *fdset):用于在文件描述符集合中增加一个新的文件描述符。 

FD_CLR(fd_set *fdset):用于在文件描述符集合中删除一个文件描述符。 (windows上测试的时候,并没有清除掉

FD_ISSET(int fd,fd_set *fdset):用于测试指定的文件描述符是否在该集合中。

13、epoll

https://www.cnblogs.com/aspirant/p/9166944.html

Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你。如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率。

Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你。这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。

select、poll、epoll区别

select==>时间复杂度O(n)

它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

poll==>时间复杂度O(n)

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.

epoll==>时间复杂度O(1)

epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

理解

假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面。如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的。

进一步解释select和epoll模型的差异:

select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了。

int n = select(&readset,NULL,NULL,100);
 for (int i = 0; n > 0; ++i)
 {
        if (FD_ISSET(fdarray[i], &readset))
        { 
               do_something(fdarray[i]);
               --n; 
        } 
}

epoll版大妈就比较先进了,她记下了同学甲的信息,比如说他的房间号,那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了。

n = epoll_wait(epfd,events,20,500);
 for(i=0;i<n;++i) 
{ 
        do_something(events[n]); 
}

在epoll中,关键的数据结构epoll_event定义如下:

typedef union epoll_data 
{ 
     void *ptr; 
     int fd;
      __uint32_t u32;
      __uint64_t u64;
} epoll_data_t;

struct epoll_event
{ 
     __uint32_t events; /* Epoll events */
     epoll_data_t data; /* User data variable */ 
};

可以看到,epoll_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针,等等。

有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲。别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一。

再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了。

数据结构

首先epoll_create创建一个epoll文件描述符,底层同时创建一个红黑树,和一个就绪链表;红黑树存储所监控的文件描述符的节点数据,就绪链表存储就绪的文件描述符的节点数据;epoll_ctl将会添加新的描述符,首先判断是红黑树上是否有此文件描述符节点,如果有,则立即返回。如果没有, 则在树干上插入新的节点,并且告知内核注册回调函数。当接收到某个文件描述符过来数据时,那么内核将该节点插入到就绪链表里面。epoll_wait将会接收到消息,并且将数据拷贝到用户空间,清空链表。对于LT模式epoll_wait清空就绪链表之后会检查该文件描述符是哪一种模式,如果为LT模式,且必须该节点确实有事件未处理,那么就会把该节点重新放入到刚刚删除掉的且刚准备好的就绪链表,epoll_wait马上返回。ET模式不会检查,只会调用一次

此处重点谈一下ET和LT模式,ET模式是将就绪的事件传给用户的event结构体数组后,在将事件传给一个列表txlist,进而返回给就绪列表,下一次传递会再检测事件用户是否完全接收数据,将未完全接收就绪事件再次传递给用户,重复步骤,直至用户全部接受数据为止。

LT模式为将就绪事件传递给用户之后,不必返回给就绪列表,可能存在丢失数据的可能。

14、TCP的可靠和有序

14.1、超时重传

原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。

影响超时重传机制协议效率的一个关键参数是重传超时时间(RTO,Retransmission TimeOut)。RTO的值被设置过大过小都会对协议造成不利影响。

  • RTO设长了,重发就慢,没有效率,性能差。
  • RTO设短了,重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

连接往返时间(RTT,Round Trip Time),指发送端从发送TCP包开始到接收它的立即响应所消耗的时间。

14.2、阻塞控制

拥塞控制是一个全局性的过程; 流量控制是点对点通信量的控制。TCP拥塞控制4个核心算法:慢开始(slow start)、拥塞避免(Congestion Avoidance)、快速重传(fast retransmit)、快速回复(fast recovery)。拥塞窗口(cwnd,congestion window),其大小取决于网络的拥塞程度,并且动态地在变化。

慢开始( slow-start ):当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。另,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。

拥塞避免( congestion avoidance ):让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:

  • 当cwnd < ssthresh时,使用慢开始算法
  • 当cwnd > ssthresh时,改用拥塞避免算法
  • 当cwnd = ssthresh时,慢开始与拥塞避免算法任意

无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限设置为出现拥塞时的发送窗口大小的一半。然后把拥塞窗口设置为1,执行慢开始算法。如下图:

拥塞控制的具体过程如下:

  1. TCP连接初始化,将拥塞窗口设置为1
  2. 执行慢开始算法,cwnd按指数规律增长,直到cwnd=ssthresh时,开始执行拥塞避免算法,cwnd按线性规律增长
  3. 当网络发生拥塞,把ssthresh值更新为拥塞前ssthresh值的一半,cwnd重新设置为1,按照步骤(2)执行

乘法减小和加法增大

  • 乘法减小:是指不论在慢开始阶段还是拥塞避免阶段,只要出现超时,就把慢开始门限减半,即设置为当前的拥塞窗口的一半(于此同时,执行慢开始算法)。当网络出现频繁拥塞时,ssthresh值就下降的很快,以大大将小注入到网络中的分组数。
  • 加法增大:是指执行拥塞避免算法后是拥塞窗口缓慢增大,以防止网络过早出现拥塞。

快速重传(Fast retransmit):要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到3个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计数器时间到期。

快速恢复(Fast Recovery):

  • 当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。
  • 由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
发送方窗口的上限值 = Min [ rwnd, cwnd ]
当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大

14.3、流量控制(滑动窗口)

大家都知道,我们从一台机器向另外一台机器发送数据的时候,数据并不是一口气也不可能一口气传输给接收方。这个并不难理解,因为网络环境特别的复杂,有些地方快有些地方慢。所以,操作系统把这些数据写成连续的数据包,并且以一定的速率发给对方。一定的速率怎么理解呢?网络环境就像复杂的交通链路。就好比一个沙漏,中间可能有一个地方流量非常的小,这个最小的口径决定了网络传输的真正速度。我们要考虑到带宽缓冲区等因素,如果一下子发送所有的数据只会加大网络压力,造成丢包重试,轻则传输更慢,重则网络崩溃。因为TCP是顺序发送的,操作系统将这些数据包一批一批的发送给对方,就像一个窗口,不停地往后移动,所以,我们称之为TCP滑动窗口协议。

在TCP中,窗口的大小是在TCP三次握手后协定的,并且窗口的大小并不是固定的,而是会随着网络的情况进行调整。这个也不难理解,原本你女朋友在家独享10M的宽带,你下班要上虎牙看网址荣耀直播,两个人就要共享这个宽带。TCP为了更好的传输效率,就会调整窗口的大小。

我们通过一个图来解释下滑动窗口的工作流程,对于发送端来说,即将要发送的数据包排成一个队列,对于发送者来说,数据包总共分成四类。分别是在窗口前的,已经发送给接收方,并且收到了接收方的答复,我们称之为已发送。在窗口中的,有两种状态,一个是已经发送给接收方,但是接收方还没确认送达,我们称之为已发送未确认,另外一个是可以发送了,但是还没有发送,我们称之为允许发送未发送。最后的是在窗口外面的,我们称之为不可发送,除非窗口滑到此处,否则不会进行发送。

就这样,一旦前面的数据已经得到服务端确认了,这个窗口就会慢慢地往后滑,如下图所示,P1,P2两个数据包被确认之后,窗口就往后移动,后面新的数据包就由不可发送待发送变成了可发送状态了。

TCP的滑动窗口协议有什么意义呢?首先当然是可靠性,滑动窗口只有在队列前部的被确认之后,才会往后移动,保证数据包被接收方确认并接收。其次是传输效率,假如没有窗口,服务端是杂乱无章地进行发包,因为TCP的队首效应,如果有前面的包没有发送成功,就会不停的重试,反而造成更差的传输效率。最后是稳定性,TCP的滑动窗口大小,是整个复杂网络商榷的结果,会进行动态调整,可以尽量地避免网络拥塞,更加稳定。可以关注我,后面我们在一起学习,一起分享。大家的支持是我继续唠嗑的动力。

拥塞控制和流量控制区别

  • 拥塞控制:防bai止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。
  • 流量控制:指点对点通信量的控制,是端到端正的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

TCP如何保证数据传输的完整性

TCP协议使用“重新发送与正向ACK”来保证数据传输的可靠性。发送方将等待一段时间,如果没有收到其发送的数据包的ACK确认信息,发送方就要重新发送。

15、UDP如何实现可靠传输

UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响。传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。

  • 添加seq/ack机制,确保数据发送到对端
  • 添加发送和接收缓冲区,主要是用户超时重传。
  • 添加超时重传机制。

详细说明:送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。

目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值