(1)说一下三次握手,为什么三次握手?


原因1:相互确认

两次握手只能保证单向连接是畅通的。
Step1       A -> B : 你好,B。
Step2       A <- B : 收到。你好,A。
这样的两次握手过程, A 向 B 打招呼得到了回应,即 A 向 B 发送数据,B 是可以收到的。
但是 B 向 A 打招呼,A 还没有回应,B 没有收到 A 的反馈,无法确保 A 可以收到 B 发送的数据。
所以经过第三次握手,才能确保双向都可以接收到对方的发送的数据。
Step3       A -> B : 收到,B。
这样 B 就能确定 B 发送给 A 的数据 A也能收到。

原因2:防止重复连接

三次握手的主要原因是为了防止旧的重复连接引起连接混乱问题。
在网络状况比较复杂或者网络状况比较差的情况下(丢包),发送方可能会连续发送多次建立连接的请求。如果 TCP 握手的次数只有两次,这个被客户端认为失效的消息到达了服务器端,对服务器端而言,以为这是一个新的请求连接消息,就向客户端发送确认并建立连接。对于客户而言,它认为没有给服务器再次发送连接请求(因为上次的通话已经结束)所以客户不会理睬服务器的这个确认,但是服务器则会一直等待客户的消息而处于忙等状态。这就导致了服务器资源被浪费。

所以如果 TCP 是三次握手的话,因为客户没有向服务器发出确认(即第三次握手),由于服务器一段时间内收不到确认,就知道客户没有要求建立连接从而不会一直处于忙等状态。
注:
第一次握手:SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。
第二次握手:这个ACK报文段也不能携带数据,但同样要消耗掉一个序号。
第三次握手:ACK报文段可以携带数据,但如果不携带数据则不消耗序号。

(2)说一下项目中你的报文怎么处理的,产生粘包会怎么样呢,用类似于数据链路层的透明传输的思路去解决?

在TCP的socket编程中,发送端和接收端都有成对的socket。发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法),将多次间隔较小、数据量较小的数据,合并成一个数据量大的数据块,然后进行封包。那么这样一来,接收端就必须使用高效科学的拆包机制来分辨这些数据。

1.Q:什么是TCP粘包问题?

TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

2.Q:造成TCP粘包的原因?

(1)发送方原因 
TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:
只有上一个分组得到确认,才会发送下一个分组
收集多个小分组,在一个确认到来时一起发送
Nagle算法造成了发送方可能会出现粘包问题
(2)接收方原因
TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

3.Q:什么时候需要处理粘包现象?

如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时当然不需要处理粘包现象
如果多个分组毫不相干,甚至是并列关系,那么这个时候就一定要处理粘包现象了

4.Q:如何处理粘包现象?

(1)发送方
对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
(2)接收方
接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。
(2)应用层
应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。
解决办法:
方法1:对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满。
方法2:对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象。
方法3:由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

以上提到的三种措施,都有其不足之处:
第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。
第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。
第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。
——但是如何判断每条报文长度和报文的位置
方法1:格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
方法2:发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。(更推荐)

5.Q:UDP会不会产生粘包问题呢?

UDP不存在粘包问题,是由于UDP发送的时候,没有经过Negal算法优化,不会将多个小包合并因此发送出去。另外,在UDP协议的接收端,采用了链式结构来记录每一个达到UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时执行了多大的缓冲区)。
举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

PS:TCP是面向字节流的传输方式,而UDP是面向报文的
面向报文是指应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,即不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。
面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。

(3)TCP怎么做到数据传输不出差错的?

TCP协议保证数据传输可靠性的方式主要有:
  1. 校验和
  2. 序列号
  3. 确认应答
  4. 超时重传
  5. 连接管理
  6. 流量控制
  7. 拥塞控制

(1)校验和

计算方式:在数据传输的过程中,将发送的数据段分成一个个16比特字。将这些16比特字加起来。并且前面的进位不能丢弃,补在后面(回卷),最后取反,得到校验和。
发送方:在发送数据之前计算检验和,并进行校验和的填充。
接收方:收到数据后,对数据以同样的方式对数据段分成的比特字进行相加回卷计算(不用取反),将计算结果和发送方的校验和相加,如果没有出现差错那么最后的结果应该是全1,否则表示报文段出现差错。

注意:如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。(有概率先数据出错但是校验码校验成功的情况)

(2)序列号(3)确认应答

序列号:TCP传输时将每个字节的数据都进行了编号,这就是序列号。
确认应答:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号ack,接收方期望发送方发送的下一个报文段的第一个数据字节的序号(隐含表示对在这之前的所有数据(<=ack-1)的确认接收)。

PS:序列号的作用不仅起应答的作用,而且序列号还能够保证接收的报文是有序且不重复不丢失的。这也是TCP传输可靠性的保证之一。

(4)超时重传

在进行TCP传输时,由于确认应答与序列号机制,也就是说发送方发送一部分数据后,都会等待接收方发送的ACK报文,并解析ACK报文,判断数据是否传输成功。如果发送方发送完数据后,迟迟没有等到接收方的ACK报文,这该怎么办呢
首先,发送方没有介绍到响应的ACK报文原因可能有两点:
1.数据在传输过程中由于网络原因等直接全体丢包,接收方根本没有接收到。
2.接收方接收到了响应的数据,但是发送的ACK报文响应却由于网络原因丢包了。
TCP在解决这个问题的时候引入了一个新的机制,叫做超时重传机制。简单理解就是发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送。
如果是第一个原因,接收方收到二次重发的数据后,便进行ACK应答。
如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号),那么直接丢弃,仍旧再次发送ACK应答。

那么发送方发送完毕后等待的时间是多少呢如果这个等待的时间过长,那么会影响TCP传输的整体效率,如果等待时间过短,又会导致频繁的发送重复的包。如何权衡
由于TCP传输时需要保证能够在任何环境下都有一个高性能的通信,因此这个最大超时时间(也就是等待的时间)是动态计算的。
karn算法根据旧的RTT1和新的RTT2得到加权平均往返时间RTTs,再计算出RTT的偏差的加权平均值RTTd。最后得到超时重传时间RTO=RTTs+4*RTTd,而且当报文段每重传一次就把RTO增大一些,典型做法是  
               取新的RTT为旧的RTT的2倍。
例:在Linux中(BSD Unix和Windows下也是这样)超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待2*500ms的时间后,再次重传。等待4*500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。

(5)连接管理

连接管理就是三次握手与四次挥手的过程,保证可靠的连接,是保证可靠性的前提。
TCP三次握手:

第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
TCP四次挥手:

1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

(6)流量控制

接收端在接收到数据后,对其进行处理。如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致丢包的一系列连锁反应,超时重传呀什么的。而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。
在TCP协议的报头信息当中,有一个16位字段的窗口大小在介绍这个窗口大小时我们知道,窗口大小的内容实际上是接收端接收数据缓冲区的剩余大小。这个数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。

注:16位的窗口大小最大能表示65535个字节(64K),但是TCP的窗口大小最大并不是64K。在TCP首部中40个字节的选项中还包含了一个窗口扩大因子M,实际的窗口大小就是16位窗口字段的值左移M位。每移一位,扩大两倍。

(7)拥塞控制

TCP的四种拥塞控制算法:
  1. 慢开始
  2. 拥塞控制
  3. 快重传
  4. 快恢复
(1)慢启动开始开始发送方拥塞窗口cwnd=1,之后每发发送一条报文段且收到ACK后将cwnd+1,所以慢开始每一轮发送增长一倍,cwnd呈指数型快速增长,直到重传或者到达门限ssthresh。
(2)拥塞控制每经过一个传输轮次,拥塞窗口cwnd=cwnd+1,使拥塞窗口cwnd按线性规律缓慢增长,直到出现重传。
(3)快重传:要求接收方不要等待自己发送数据时才捎带确认,而是要立刻发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认,发送方一旦收到3个连续的重复确认,就将相应的报文段立即重传,而不是等待该报文段的超时计时器超时再重传(快重传之后配合快恢复,对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞而将cwnd降低为1,而是将ssthresh=cwnd/2且设置新的cwnd=ssthresh并线性增长,使用快重传可以整个网络吞吐量提高约20%)
(4)快恢复:发送方调整门限值ssthresh=cwnd/2,同时设置拥塞窗口cwnd=ssthresh,并开始执行拥塞避免算法。

TCP传输的过程中,发送端开始发送数据的时候,如果刚开始就发送大量的数据,那么就可能造成一些问题。网络可能在开始的时候就很拥堵,如果给网络中在扔出大量数据,那么这个拥堵就会加剧。拥堵的加剧就会产生大量的丢包,就对大量的超时重传,严重影响传输。
所以TCP引入了慢启动的机制,在开始发送数据时,先发送少量的数据探路。探清当前的网络状态如何,再决定多大的速度进行传输。这时候就引入一个叫做拥塞窗口的概念。发送刚开始定义拥塞窗口为 1,每次收到ACK应答,拥塞窗口加 1。在发送数据之前,首先将拥塞窗口与接收端反馈的窗口大小比对,取较小的值作为实际发送的窗口。
拥塞窗口的增长是指数级别的。慢启动的机制只是说明在开始的时候发送的少,发送的慢,但是增长的速度是非常快的。为了控制拥塞窗口的增长,不能使拥塞窗口单纯的加倍,设置一个拥塞窗口的阈值,当拥塞窗口大小超过阈值时,不能再按照指数来增长,而是线性的增长。在慢启动开始的时候,慢启动的阈值等于窗口的最大值,一旦造成网络拥塞,发生超时重传时,慢启动的阈值会为原来的一半(这里的原来指的是发生网络拥塞时拥塞窗口的大小),同时拥塞窗口重置为 1。


拥塞控制是TCP在传输时尽可能快的将数据传输,并且避免拥塞造成的一系列问题。是可靠性的保证,同时也是维护了传输的高效性。

(4)web页面请求过程?

1.为主机申请IP:DHCP动态主机配置协议

首先需要一个IP地址,告诉网络我是谁。这就需要DHCP动态主机配置协议。
1) 主机的操作系统生成一个DHCP请求报文
2) 并将这个报文放入具有目的端口67(DHCP服务器的固定端口)和原端口68(DHCP客户端的固定端口)的UDP报文段。
3) 该UDP报文段则被放置在一个具有广播IP目的地址(255.255.255.255)和源IP地址(0.0.0.0)的IP数据报中。
4) 在数据链路层添加头部封装成包含DHCP请求的广播以太网帧,该以太网帧包含源MAC地址(自己的MAC)和目的MAC地址(FF:FF:FF:FF:FF:FF)。
5) 以太网帧被发送到交换机,交换机修改转发表记录我的MAC (自学习:每接收到一个帧都记录该帧的MAC和到达的接口),然后在他的所有出口广播这个帧。
6)与交换机相连的默认网关路由器接收到了这个广播帧,进行解析,提取出IP数据报,发现目的IP是广播IP,就交给传输层,传输层又提取出 DHCP 请求交给应用层, DHCP服务器就收到了该 DHCP 请求(DHCP服务器一般运行在路由器中,路由器的路由功能只是它众多功能的一种,他还可以支持别的协议,比如这里的DHCP协议)。
7)DHCP 服务器为此生成一个 DHCP ACK 报文,主要包含:分配给DHCP请求的IPDNS服务器的IP默认网关路由器的IP子网掩码;这个报文再被传输层、网络层、数据链路层一路封装成帧,该帧的目的MAC 是我的MAC,源MAC是接收 DHCP 请求帧的路由器端口的 MAC
8)DHCP ACK以太网帧由默认网关路由器发送给交换机,交换机根据转发表转发回给我的主机
9)主机收到该帧之后再从链路层到应用层,层层提取,最后得到DHCP ACK报文,从而记录自己的IP、DNS服务器IP、默认网关路由器IP,子网掩码。

2.查找默认网关路由器的MAC地址:ARP地址转换协议

在前面 DHCP 过程中我们已经得到了 DNS 服务器的 IP 地址,现在只要去访问该IP就好,但是在这之前我们需要先走出我们所处的局域网,这就需要局域网默认网关路由器的MAC地址(通过默认网关离开主机所在的局域网)
为什么需要局域网的MAC地址呢 
因为局域网内是通过 MAC 进行识别的,为什么要用 MAC 地址识别呢?是这样的,以前没这么多主机的时候,IP 是固定的,我们就不需要 MAC,但现在主机越来越多,这就导致局域网里 IP 不是十分充足,管理起来也不是很好管理,所以 IP 每隔一段时间就会被回收,需要的时候才会被分配,这也就是为什么前面提到的 DHCP 动态主机配置协议会存在,所以这个 IP 是会变的,对于主机来说,唯一不变的是 MAC,所以,在局域网内部我们是用 MAC 定位的。
10)主机生成一个 ARP 查询报文,目的 IP 是默认网关路由器,这个报文最终被封装成以太网帧,帧的目的MAC是 FF:FF:FF:FF:FF:FF(广播地址),然后把帧发给交换机,交换机看到是广播地址就给广播出去。
11)默认网关路由器接收到了这个帧,经过层层拆封提取得到 ARP 报文,发现其中的目的 IP 跟他自己某个接口的 IP 匹配,就发送回去一个 ARP应答报文给主机,这里包含了他自己的MAC地址。

3.查找目的域名的IP:DNS域名系统

现在我们拿到了默认网关路由器的MAC,可以离开局域网去DNS服务器查目的域名的IP了
12)主机上的操作系统生成了包含目的域名的DNS查询报文,并将该DNS查询报文放置在一个具有53号目的端口(DNS服务器目的端口)的UDP报文段中,层层封装成以太网帧,帧的目的 MAC 是默认网关路由器的 MAC地址。
13)默认网关路由器接收到该帧之后,提取出IP数据报,并根据路由表进行转发,因为路由器具有内部网关协议*(RIP路由信息协议、OSPF开放最短路径优先协议)和外部网关协议(BGP边界网关协议)*,因此路由表中已经配置了可以从路由器到达 DNS 服务器的路由表项。
14)DNS 服务器接收到帧后,层层提取出 DNS 查询报文,并在 DNS 数据库中查找待解析域名对应的 IP,找到之后发送 DNS 应答报文,封装成 UDP 报文段,再放入 IP 数据报,最后通过路由器发给源主机的默认网关路由器,再经由交换机转发给源主机。

4.web客户—服务器交互:TCP和HTTP

有了目的域名的IP后就可以访问HTTP服务器了,有了HTTP服务器的IP地址之后,主机操作系统能够生成TCP套接字,该套接字将用于向HTTP服务器发送HTTP GET报文
15)主机中的TCP首先与HTTP服务器中的TCP进行三次握手来建立TCP连接(生成一个具有80号目的端口的TCP SYN 报文段,并发送给HTTP服务器)。
16)包含TCP SYN的数据报到达HTTP服务器后,从数据报中读出TCP SYN报文并分解到与端口号80相联系的欢迎套接字。对于HTTP服务器与我的主机之间的TCP连接生成一个连接套接字,生成一个TCP SYNACK报文段,发回给主机后从而建立连接。
17)连接建立之后,浏览器生成一个HTTP GET报文写入TCP套接字中。
18)HTTP服务器从TCP套接字读取HTTP GET报文,生成一个HTTP响应报文,将Web页面内容放入报文主体中,并将报文发送进TCP套接字中。
19)浏览器从套接字读取HTTP响应报文后,从HTTP响应报文中抽取Web网页的html,并最终显示web网页。

(5)UDP和TCP传包的过程?

TCP 提供的是面向连接,可靠的面向字节流服务。即客户和服务器交换数据前,必须现在双方之间建立一个TCP连接,之后才能传输数据。并且提供超时重发,检验数据,流量控制等功能,保证数据能从一端传到另一端不重复不丢失不失序。
UDP 是一个简单的面向数据报的运输层协议。它不提供可靠性,只是把应用程序传给IP层的数据报发送出去,但是不能保证它们能到达目的地。由于UDP在传输数据报前不用再客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快。

TCP套接字编程


TCP编程的服务器端一般步骤是:
  1. 创建一个socket,用函数socket();
  2. 设置socket属性,用函数setsockopt(); * 可选
  3. 绑定IP地址、端口等信息到socket上,用函数bind();
  4. 开启监听,用函数listen();
  5. 接收客户端上来的连接,用函数accept();
  6. 收发数据,用函数send()和recv(),或者read()和write();
  7. 关闭网络连接;
  8. 关闭监听;
TCP编程的客户端一般步骤是:
  1. 创建一个socket,用函数socket();
  2. 设置socket属性,用函数setsockopt(); * 可选
  3. 绑定IP地址、端口等信息到socket上,用函数bind(); * 可选
  4. 设置要连接的对方的IP地址和端口等属性;
  5. 连接服务器,用函数connect();
  6. 收发数据,用函数send()和recv(),或者read()和write();
  7. 关闭网络连接;

UDP套接字编程


UDP编程的服务器端一般步骤是:
  1. 创建一个socket,用函数socket();
  2. 设置socket属性,用函数setsockopt();* 可选
  3. 绑定IP地址、端口等信息到socket上,用函数bind();
  4. 循环接收数据,用函数recvfrom();
  5. 关闭网络连接;

UDP编程的客户端一般步骤是:
  1. 创建一个socket,用函数socket();
  2. 设置socket属性,用函数setsockopt();* 可选
  3. 绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4. 设置对方的IP地址和端口等属性;
  5. 发送数据,用函数sendto();
  6. 关闭网络连接;

(6)UDP和TCP的编写过程?



(7)说一下网络的拥塞控制?




(9)说一下Dijkstra算法?