网络传输层UDP/TCP

  • 引入
    • ①端口号
    • ②端口号划分
    • ③知名端口号
    • ④netstat、pidof、killall、ps、 awk、xargs????
    • ⑤所有协议需要解决的两个问题:
  • 1)UDP
    • ①UDP协议段格式
    • ②UDP缓冲区
    • ③UDP特点及注意事项
    • ④基于UDP的协议
  • 2)TCP
    • 引入
    • ①TCP协议段格式
      • 32位序号 和 32位确认序号
      • 16位窗口大小
      • 16位紧急指针
      • 6位标志位(16位紧急指针)
    • ②确认应答(ACK)机制(Linux内核 MARK)
    • ③超时重传机制
    • ⑤TIME_WAIT CLOSE_WAIT (MARK)
    • ⑥滑动窗口(发送方)
      • 理解滑动窗口
      • 高速重发控制
    • ⑦流量控制(接收方)
    • ⑧拥塞控制(网路中)(Reno NewReno MARK)
    • ⑨延迟应答 和 捎带应答
    • ⑩其他
      • TCP异常情况
      • 基于TCP的应用层协议
  • 3)面向字节流
  • 4)粘包问题
  • 5)总结
    • ①TCP总结
    • ②TCP对比UDP
    • ③UDP实现可靠性
    • ④listen的第二个参数backlog
    • ⑤wireshark抓包

引入

①端口号

端口号标识一个主机上进行通信的不同程序
在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信

②端口号划分

  • 0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的
  • 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的

③知名端口号

比如:

  1. ssh服务器, 使用22端口
  2. ftp服务器, 使用21端口
  3. telnet服务器, 使用23端口
  4. http服务器, 使用80端口
  5. https服务器, 使用443
  6. sunproxyadmin: 使用8081端口

执行命令vim /etc/services可以查看端口号

④netstat、pidof、killall、ps、 awk、xargs????

netstat:
语法: netstat [选项]
功能:查看网络状态
常用选项:

  1. -n 拒绝显示别名,能显示数字的全部转化成数字
  2. -l 仅列出有在 Listen (监听) 的服務状态
  3. -p 显示建立相关链接的程序名
  4. -t (tcp)仅显示tcp相关选项
  5. -u (udp)仅显示udp相关选项
  6. -a (all)显示所有选项,默认不显示LISTEN相关

例如tcp:

pidof 【进程名】:查看进程的pid

killall
功能:kill和ps grep等的结合体,用来使用进程名称来杀死进程
参数:

  1. -e:对长名称进行精确匹配;
  2. -l:忽略大小写的不同
  3. -p:杀死进程所属的进程组;
  4. -i:交互式杀死进程,杀死进程前需要进行确认;
  5. -l:打印所有已知信号列表
  6. -q:如果没有进程被杀死。则不输出任何信息;
  7. -r:使用正规表达式匹配要杀死的进程名称;
  8. -s:用指定的进程号代替默认信号“SIGTERM”;
  9. -u:杀死指定用户的进程

ps axj | grep 【进程名】:查看为【进程名】的所有进程
ps axj | hread -nl && ps axj | grep 【进程名】
ps axj | grep 【进程名】| grep -v grep | awk ‘{print $2}’ | xargs kill -p//第二列
gawk 列处理工具
输入记录中的每个字段都可以通过其位置来引用:$1、$2 等等。 $0 是整个记录。字段不需要被常量引用: n = 5 print $n 打印输入记录中的第五个字段
xargs :Linux xargs 命令

⑤所有协议需要解决的两个问题:

需要解决:

  1. 如何将自己的报头和有效载荷分离的问题
  2. 任何协议都必须解决,要将自己的有效载荷交付给,上层的哪一个协议

1)UDP

①UDP协议段格式


16位UDP长度, 表示整个数据报(UDP首部+UDP数据)的最大长度;如果校验和出错, 就会直接丢弃
所有协议需要解决的两个问题(对于UDP):

  1. 通过定长报头
  2. 16位目的端口号
    1.为什么socket编程中的port是16位的因为协议是16位的 2.为什么server要绑定,因为需要向上层协议交付

UDP或者TCP是在内核里面,tcp/ ip协议栈,本质也是Linux内核的一部分
由于Linux内核是用C语言实现的-->内核中,我们的报头其实是位段

struct udp_header {uint32_t src_ port: 16uint32_t dst_ port:16;uint32_t udp_ length: 16:uint32_t udp_ check: 16;
}

位段是一种类型,可以定义变量->开辟空间,保存数据->填充报头本质上就是给位段类型对应的变量赋值

②UDP缓冲区

sendto, recvfrom,read,write,send, recv等本质并不是把数据发送到网络中,而是将用户数据拷贝到udp/tcp的发送缓冲区,或者将内核缓冲区中的数据,拷贝到用户
OS中的传输层协议来决定什么时候发送数据,发送多少数据,丢包了该干嘛(UDP丢包了不管)


UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;
UDP的socket能同时读写,全双工

③UDP特点及注意事项

UDP传输的过程.

  1. 无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
  2. 不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;(注意 不可靠是 中性词)
  3. 面向数据报: 不能够灵活的控制读写数据的次数和数量

注意事项:

  • UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部).,然而64K在当今的互联网环境下, 是一个非常小的数字.,如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装

④基于UDP的协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议

2)TCP

引入

冯诺伊曼体系结构中,(硬件与硬件之间是互相独立的)独立组件是通过“线”做到数据的传导的 即便是在本主机内部,各个设备间都要有自己的协议(功能性问题,比如SCSI HBA PCI …)


专门设计TCP/IP协议是因为通信的设备之间“线更长了,传播的距离更远了,数据更容易丢失

  1. TCP解决传输过程中的可靠性问题
  2. IP解决定位设备问题

所以万物皆可系统化,皆可网络化

①TCP协议段格式

首先:任何协议都要先解决的两个问题:

  1. 分离有效载荷 (位首部长度,定长报头+自描述报头长度4bit(包括选项) 区分报头和有效载荷)
  2. 将有效载荷传给上一层协议 ( )

TCP协议段格式:

注意:

  1. 这里的4位首部长度,单位是四字节,比如:如果报头长度20个字节,4位首部长度就是20/4=5 二进制0101
  2. 标准的TCP报头为20个字节最大报头长度为60字节(可携带选项40字节)(4位首部长度-20就可判断是否继续读取option)

32位序号 和 32位确认序号

确认应答机制(ACK)保证了可靠性

  • 不是对最新数据的可靠性保证 (最新数据可靠性不能被保证,只有收到应答才代表发出的消息被收到,可靠) ,而是对历史数据的可靠性进行保证

TCP是全双工的

  • 需要保证双向可靠性,双方需要确认应答机制,并遵守

host1向host2发送消息不一定是发一条,host2回一条,可能是发送多条,host2再回复多条,在这个过程中,每个报文可能选择的路由路径都是不同的,不同路径的通畅程度也不同(不可靠)

  • 所以,在TCP的报头中有序号,可以对报文进行顺序重排(保证按序到达)

无论是请求还是应答都是:

  • tcp数据段:tcp报文+有效载荷 (应答可能没有有效载荷)

为什么要有一对32位序号?

  • 任何请求可能是应答也可能是数据发送,所以,需要确认序号,来对对方的报文进行确认,同时也需要序号来保证报文到对方的按序到达


注意图中没有体现出带字节数据的情况


32位序号:(参考关于ISN)

  • 初始序列号(ISN),是客户端随机产生的一个值,初始序列号(ISN)随时间而变化的,而且不同的操作系统也会有不同的实现方式 之后序号就是对方的确认序号

32位确认序号(32位序号+1):

  • 告诉发送者下一次从哪里开始发
    注意:确认号是第一次发送的时候是0之后是对方发来的序号+1
    (比如发送方发送了一个报文段序号为301的TCP段,这个段携带了100字节数据,则接收方应当回复的确认号是401)

16位窗口大小

TCP有自己的发送缓冲区和接受缓冲区,结合到之前的socket编程

  1. write,send…本质是将用户数据拷贝到TCP的发送缓冲区
  2. read,recv…本质是将内核的TCP缓冲区数据,拷贝到用户的缓冲区

当发送的数据过大,对方来不及接收, 16位窗口,表明自己的接收能力(接收缓冲区中剩余的空间大小)
站在OS的角度:

这种通过互相通报自己的接收能力(16位窗口大小)给对方,以达到两个方向上的传输速度的控制叫做流量控制
同时也可以看作在网络层面上生产者消费者的应用

16位紧急指针

因为tcp是按序到达的, 所以,如果想让后续的数据被优先读取并处理,是不可能的,所以TCP提供了优先处理数据的能力,就是设置TCP中的URG标志位,紧急数据标志位,结合紧急指针使用(只能有一字节的紧急数据)

在socket编程中send和recv有一个flag选项:可以填写MSG_OOB来设置紧急数据和优先处理紧急数据

  • send
  • recv

6位标志位(16位紧急指针)

标志位(0 or 1) 作用
URG(urgent) 紧急指针是否有效
ACK(acknowledgement) 确认号是否有效
PSH(push) 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST(reset) 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN(synchronous) 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN(finish) 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段

为什么一定要SYN:

  • 服务器收到tcp报文的时候,一定会在同一时间段,收到多种报文,为了保证数据正常通信,此时接收方就使用SYN如何区分, 这些报文中,哪些是断开链接,哪些是建立链接的

解释RST:

  • 在三次握手场景中如果最后一次ACK失败,client会仍然认为建立是成功的(第三次没有应答),server会认为建立链接失败,在这时,认为成功的client就会向server发送数据,server在建立连接失败的情况下收到数据就会向client发送RST=1,要求重新建立连接

为什么一定是三次握手:

  • 假设我们是4次握手,也就是多了一次ACK,但是更重要的是,认为建立连接成功的一方由client变为了server
    连接可能很多,OS需要管理,(维护一个链接是有成本的(时间+空间), 且建立连接并不是必须100%建立成功,如果黑客想要攻击server,只需要向server发起大量的连接请求,但是在最后一次时不接收server的ACK,这样server认为建立成功,增加大量维护连接的成本,导致瘫痪,所以我们必须使用奇数次的握手
  • TCP是全双工的,需要SYN - > SYN+ACK 一来一回两次验证网络信道的通畅程度,所以三次是成本最低的选择

②确认应答(ACK)机制(Linux内核 MARK)

TCP将每个字节的数据都进行了编号. 即为32位序列号(sequence number)
TCP发送与接收缓冲区,可以简单理解为字符数组char send_buffer[MAXSIZE], char recv_buffer[MAXSIZE], 数组下标就是天然的序列号(操作系统中是用其他方式实现的)

③超时重传机制

有两种情况会触发超时重传机制:

1.ACK丢包

  • 当在间隔时间内client向server发送数据,仍会触发RST
  • 重传SYN+ACK重复的解决方案(TCP层有去重能力,每个报文都有序列号)

2.普通丢包


间隔时间:

  1. 但是这个时间的长短, 随着网络环境的不同, 是有差异的.
  2. 如果超时时间设的太长, 会影响整体的重传效率;
  3. 如果超时时间设的太短, 有可能会频繁发送重复的包

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.

  1. Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.
  2. 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
  3. 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
  4. 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接

⑤TIME_WAIT CLOSE_WAIT (MARK)

先看TCP三次握手和四次挥手:
三次握手:

过程:

  1. server:[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
  2. client:[CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段
  3. server:[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文
  4. client:[SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据;
  5. server:[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了.

注意:

  1. connect本质是发起三次握手,但是应用层并不干涉甚至关心三次握手,只关心connect返回值
  2. accept返回的本质,一定是底层已经握手完成之后,然后就可以返回对应的链接
    .

写数据:

  • write数据,本质其实只是把数据拷贝到发送缓冲区,然后由tcp来决定什么时候发,发多少,发给谁等诸多问题!

四次挥手:

过程:

  1. client:[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1;
  2. server:[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接, 服务器会收到结束报文段, 服务器返回确认报文段并进CLOSE_WAIT;
  3. client:[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段;
  4. server:[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  5. client:[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;
  6. server:[LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接
  7. client:[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态

注意:

  • 执行close(fd),不一定明确执行close().,如果有任何一个进程退出的时候,OS会自动关闭该进程打开的文件----文件描述符的生命周期是随进程的

之前在socket编程的时候会经常出现一个问题:bind error(绑定失败),在server断开后马上绑定同一端口进行启动:
因为有TIME_WAIT,netstat命令查看

注意(TIME_WAIT:主动断开连接的一方):

  1. TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态.
  2. 我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方, 在TIME_WAIT期间仍然不能再次监听同样的server端口;
  3. MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在CentOS上默认配置的值是60s,可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值

为什么要有TIME_WAIT:

  1. 为了保证历史数据在网络中消散
  2. 四次挥手依旧是最担心最后一个ACK丢失(较大概率保证最后一个ACK被对方收到,.从而较大概率保证链接是正确四次挥手的)

bind error解决方案:

  • 创建好套接字之后加上这两句代码
int opt=1;
setsockopt(listenfd, SOL_SOCKET ,SO_REUSEADDR, &opt, sizeof (opt));

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
具体参考:setsockopt的常用选项

CLOSE_WAIT:被动断开连接的一方
server端断开:
注意:对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题
客户端断开:

⑥滑动窗口(发送方)

引入:

  • 发送方在发送数据的时候,不一定完成了发送过程,在没有收到ACK的时候,需要暂时将这些数据保存起来
    发送缓冲区分为三部分(已发送的(可被覆盖)可以发送或还没收到ACK尚未发送),其中第二部分为滑动窗口

对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段.这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候, 如果一次发送多条数据, 就可以大大的提高性能

假如窗口大小为4,就可以省去第(1,2,3 )(5,6,7)… 即次发送的前三个包的时间
注意:

  1. 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值. 上图的窗口大小就是4000个字节(四个段)(大小取决于对方的win).
  2. 发送前四个段的时候, 不需要等待任何ACK, 直接发送
  3. 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
  4. 操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;
  5. 窗口越大, 则网络的吞吐率就越高

理解滑动窗口

问1:窗口可否缩小扩大?

  • :可以:
  • 解释:在发送数据后,对方的上层没有将数据取走,ACK回应包中,win变小,滑动窗口变小,反之扩大

问2:如何理解并设计滑动窗口?

  • tcp是面向字节流的,tcp的发送缓冲区可以看作一个char类型的send_buffer数组
    (滑动窗口的本质是一段范围,可以定义一个start指向数组的一个下标标识滑动窗口最左边,end指向另一个数组下标标识滑动窗口最右边)
  • 当start=end时滑动窗口大小为0
  • 当收到包中确认序号ACK Num为2001时,start就指向数组下标2001位置,end = start+win(窗口大小)

问3:滑动窗口一直向右移动,缓冲区大小有限,会不会移出缓冲区?

  • :不会
  • 解释:tcp的接收缓冲区是环状结构,end如果加完start后到达结尾,需要进行一次mod操作,回到发送缓冲区的第一部分(已经发送的数据可以被覆盖了)

问4:在开始还没有和对方建立连接的时候,不能知道对方的接收能力,发送缓冲区的大小取决于对方接收缓冲区的大小,如何得知的?

  • 从握手就开始进行协商双方的接收和发送能力

高速重发控制

ACK丢包:

  • 这种情况下并不影响,因为总是根据最后一次ACK进行设置滑动窗口的start (ACKNum之前的数据肯定已经被接收方接收到了)
  • 在下一次发送数据包的时候,并没有收到上一次发送数据包 的前三个ACK,但收到了最后一个ACK,代表前三个数据包已被接收方收到

数据包丢失:

  • 当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样;
  • 如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;
  • 这个时候接收端收到了 1001 之后, 再次返回的ACK就是6001了(因为2001 - 6000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;

这种机制被称为 "高速重发控制"(也叫 "快重传")

⑦流量控制(接收方)

如果发送方发的太快,导致接收端缓冲区变满,继续发送,会造成丢包,引发丢包重传等
TCP支持根据接收端的处理能力,决定发送端的发送速度:

  1. 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;
  2. 窗口大小字段越大, 说明网络的吞吐量越高;
  3. 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
  4. 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
  5. 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.


注意

  1. TCP窗口最大不止65535字节(16位),TCP首部40字节的option中包含了一个窗口扩大因子M, 实际窗口大小是窗口字段的值左移 M 位
  2. TCP连接初始化时,通信双方使用kind=3窗口扩大因子选项来协商接收通过的窗口扩大因子, 假设TCP头部中的通告窗口大小为N,窗口扩大因子(位移数)是M,那么TCP报文段的实际接收通告窗口大小为:N * (2 M), M的取值范围为0 ~ 14
  3. 可以通过cat /proc/sys/net/ipv4/tcp_window_scaling查看linux中的M值,默认1

⑧拥塞控制(网路中)(Reno NewReno MARK)

当网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,会导致拥塞情况更加严重
TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据
下图是慢启动机制:(先指数再线性)

所谓慢启动是指数增长初始时慢
TCP Tahoe算法:

  1. 当TCP开始启动的时候, ssthresh(慢启动阈值)等于窗口最大值(cwnd)大小为 1, 2, 4 或 10 MSS
  2. 在每次超时重发的时候, 慢启动阈值会变成原来的一半(cwnd/2)同时拥塞窗口置回1,
  3. 同时进入拥塞避免(Congestion Avoidance)阶段,在该阶段下,cwnd以线性方式增长,大约每经过一个RTT(往返时延)(相当于收到一个ACK应答),cwnd的值就会加1
    RTT若小于超时重传时间RTO,则会造成不必要的重传, 若大于RTO,则会使得网络浪费太多空间,所以综上所述,最好的就是 超时重传时间RTO应略大于往返时间RTT

  • 少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
    当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
    拥塞控制, 是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案

注意: 滑动窗口=Min(win, 拥塞窗口)

⑨延迟应答 和 捎带应答

延迟应答:两个(N一般为2)报文一个ACK
捎带应答:ACK+Data(提高效率)

⑩其他

TCP异常情况

情况:

  1. 进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
  2. 机器重启: 和进程终止的情况相同.
  3. 机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.

应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ
断线之后, 也会定期尝试重新连接

基于TCP的应用层协议

HTTP HTTPS SSH Telnet FTP SMTP

3)面向字节流

创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区
由于缓冲区的存在, TCP程序的读和写不需要一一匹配

  • 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
  • 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次

4)粘包问题

包指的是应用层的数据包

  1. 在TCP的协议头中, 没有如同UDP一样的 “报文长度” 这样的字段, 但是有一个序号这样的字段.
  2. 站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中.
  3. 站在应用层的角度, 看到的只是一串连续的字节数据.,那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包

举例:http报文是通过\n来分开的,整体其实可以看作一个长字符串


解决粘包问题----明确边界:

  1. 对于定长的包, 保证每次都按固定大小读取即可; 例如上面的Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
  2. 对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置;
  3. 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可);

注意:TCP并不关心粘包问题,具体是上层应用层制定协议来保证的


UDP没有粘包问题:(UDP是面向数据报)

  1. 对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层. 就有很明确的数据边界.
  2. 站在应用层的站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况

5)总结

①TCP总结

保证可靠性:

  • 校验和
  • 序列号(按序到达)
  • 确认应答
  • 超时重发
  • 连接管理
  • 流量控制
  • 拥塞控制

提高性能:

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

其他:

  • 定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)

②TCP对比UDP

TCP和UDP没有好坏只有具体场景:

  1. TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
  2. UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广

③UDP实现可靠性

参考TCP的可靠性机制, 在应用层实现类似的逻辑


场景:聊天:

  • 实现按序到达
  • 实现丢包重传

④listen的第二个参数backlog

backlog设为2
运行6个client向server发起连接请求

有三个为ESTABLISHED的状态另外三个为SYN_SENT
解释:
Linux内核协议栈为一个tcp连接管理使用两个队列:

  1. 半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求)(后面的谁先完成三次握手就先连接,不按顺序)
  2. 全连接队列(accpetd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)

全连接队列的长度会受到 listen 第二个参数backlog的影响.

  • 全连接队列满了的时候, 就无法继续让当前连接的状态进入 established 状态了.
  • 这个队列的长度通过上述实验可知, 是 listen 的第二个参数 + 1

注意:半链接队列长度不能太大

  1. 越靠后的,等待的时间越长
  2. 维护长队列的资源不如用来提高服务器的吞吐量

⑤wireshark抓包

Linux----网络传输层UDP/TCP相关推荐

  1. 网络传输层之TCP、UDP详解

    1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间,无法了解数据未达终点的状态.因此有必要增强网络层提供服务的服务质量. 2.引入传输层的原因 面向连接的传输服务与面向 ...

  2. 网络 传输层 | UDP协议与TCP协议详解(三次握手及四次挥手、滑动窗口、拥塞控制)

    概念 传输层:是负责应用程序之间的数据传输(通过端口的描述,描述了哪两个进程间的通信):传输层的两个主要协议:UDP 和 TCP UDP协议 UDP协议全称:用户数据报协议(User Dategram ...

  3. 【TCP/IP网络协议】(五)传输层UDP协议

    文章目录 传输层协议 一.端口 二.UDP概述 三.UDP报文 四.程序测试 传输层协议 从之前介绍的网络层协议来看,通信的两端是两台主机,IP数据报首部就标明了这两台主机的IP地址,但是从传输层来看 ...

  4. 《UNIX网络编程 卷1:套接字联网API(第3版)》——第2章 传输层:TCP、UDP和SCTP 2.1概述...

    本节书摘来自异步社区<UNIX网络编程 卷1:套接字联网API(第3版)>一书中的第2章,第2.1节,作者:[美]W. Richard Stevens , Bill Fenner , An ...

  5. 27.Linux网络编程socket变成 tcp 高并发 线程池 udp

    好,咱们开始上课了,从今天开始咱们连续讲 8 天的,网络编程这个还是在linux环境下去讲,咱们先看一下咱们这 8 天都讲什么东西,跟大家一块来梳理一下,你先有个大概的印象,这些你也不要记,那么网络编 ...

  6. 【计算机网络】传输层 : 总结 ( TCP / UDP 协议 | 寻址与端口 | UDP 协议 | TCP 协议特点 | TCP 连接释放 | TCP 流量控制 | TCP 拥塞控制 ) ★★★

    文章目录 一.传输层 TCP / UDP 协议 ★ 二.寻址端口号 ★ 三.UDP 协议特点 四.UDP 协议首部格式 五.UDP 校验 六.TCP 协议 特点 ★ 七.TCP 报文段首部格式 八.T ...

  7. 传输层的TCP和UDP

    传输层的TCP和UDP TCP和UDP协议 TCP报文段 TCP的三次握手 TCP的四次断开 TCP的常用端口号及其功能 UDP的常用端口号及其功能 TCP和UDP协议 TCP(Transmissio ...

  8. 4-1:TCP协议之传输层的作用及传输层协议TCP和UDP

    文章目录 一:传输层的定义 二:通信处理 三:传输层协议 四:TCP协议的可靠和性能 一:传输层的定义 前面说过,IP首部有一个协议字段用于标识网络层(IP)的上一层采用哪一种传输层协议.根据这个字段 ...

  9. 传输层协议TCP和UDP

    本文力图简洁,让读者对TCP和UDP有个初步的认知.闲话少说,现在开始吧.TCP和UDP都是传输层的协议.TCP通过三次握手建立可靠连接,对未送达的消息重新进行发送.UDP不建立连接而直接发送,对未送 ...

最新文章

  1. 170222、使用Spring Session和Redis解决分布式Session跨域共享问题
  2. 在Servlet中获取Spring注解的bean
  3. Inno Setup入门(十八)——Inno Setup类参考(4)
  4. 软件工程——个人课程总结
  5. react元素显隐控制
  6. 如何迅速定位***路由器故障
  7. Atitit.ide技术原理与实践attilax总结
  8. HDU1865 1sting【递推】
  9. python基础-菜鸟世界 -python基础---set
  10. http转socks软件SOCKS2HTTP的使用
  11. 圆柱体积怎么算立方公式_圆柱体积计算公式 计算方法及例题
  12. ANSYS经典界面中梁单元实例全解析
  13. 复合函数求导经典例题_导数--复合函数的导数练习题
  14. badboy设置中文_badboy基本操作
  15. html 页面换皮肤,HTML中如何实现更换网页皮肤
  16. print 设置纸张的高度_祝贺! 2020珠峰高程测量登山队成功登顶!用Python计算一张纸对折多少次会超过珠峰高度?...
  17. 提升产品创新能力,试试斯坦福大学设计思维模型
  18. nginx配置域名访问/禁止ip访问
  19. mysql事务排队情况_MySQL事务问题
  20. NFT元宇宙游戏开发铸造源代码怎么写案例分享

热门文章

  1. Python 虚拟环境管理工具(详细)
  2. python的几种常用排序算法
  3. 诗歌rails 之gem命令
  4. 11 Git原理进阶
  5. LaTeX——行距问题
  6. 使用makecert.exe创建数字证书
  7. E - Obtain a Permutation
  8. NO.28——Kali Linux无线渗透暴力破解WIFI密码
  9. Flink教程(19)- Flink高级特性(BroadcastState)
  10. 南园茶社——动画展示网站