文章目录

  • 一、什么是socket(套接字)
  • 1.1 套接字的概述
  • 1.2 套接字的工作流程
  • 二、tcp/ip协议
  • 2.1 报文结构
  • 2.2 基础术语
  • 2.3 三次挥手
  • 2.3 四次挥手
  • 三、tcp和udp的区别
  • 四、linux系统tcp参数的调优
  • 面试常见问题


一、什么是socket(套接字)

1.1 套接字的概述

Socket是应用层和网络层的中间软件抽象层,它是一组接口,由ip和端口组成。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

数据交换就通过调用socket接口来进行交换。再由socket的程序来调用系统底层的数据交换。

其中socket包括基于文件类型的套接字家族(AF_UNIX),unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来获取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统完成通信

还有一种就是基于网络类型的套接字(AF_INET)

1.2 套接字的工作流程

python socketserver 心跳机制 python socket原理_python

  • 1、首先在服务端创建一个socket套接字程序
  • 2、绑定ip和端口 bind()
  • 3、监听此套接字端口 listen()
  • 4、等待和客户端建立连接 accept()(三次挥手)
  • 5、接收数据 read()
  • 6、发送数据 write()
  • 7、关闭连接 close()(四次挥手)

二、tcp/ip协议

2.1 报文结构

python socketserver 心跳机制 python socket原理_socket_02

2.2 基础术语

  • 序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号
  • 确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号
  • 确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
  • 同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0
  • 终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接

2.3 三次挥手

  • 发送端发送一个SYN=1,ACK=0;seq=x,标志的数据包给接收端,请求进行连接,这是第一次握手;
  • 接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1;seq=y,ack=x+1标志的数据包给发送端,告诉它,可以通讯了,并且让发送端发送一个确认数据包,这是第二次握手;
  • 最后,发送端发送一个SYN=0,ACK=1,seq=x+1,ack=y+1的数据包给接收端,告诉它连接已被确认,这就是第三次握手

2.3 四次挥手

  • 发送端发送FIN=1,ACK=0标志的数据包给接收端,请求释放连接
  • 接收端收到并发送ACK=1的标志数据包给发送端,确认已经无数据要发送
  • 等待接收端的数据发送完毕后,接收端发送FIN=1的数据包给发送端
  • 发送端发送确认包ACK=1
  • 接收端收到后释放链接,发送端等待2msl时间后关闭连接。

三、tcp和udp的区别

  • tcp是面向链接的,udp是面向非链接的:tcp需要建立三次握手,而udp不需要,
  • tcp是可靠传输,udp是非可靠传输:tcp发送的数据流,两次发送之间没有边界,可传输大量数据;而udp发送的数据报,两次发送之间有明确边界,每一次传送过得数据量有限
  • tcp开销大,udp开销小:tcp每次传送都需要建立链接,因此开销大,速度慢

四、linux系统tcp参数的调优

  • vi /etc/systcl.conf 添加一下内容
net.ipv4.tcp_syncookies = 1
#表示开启SYNcookies,当出现SYN队列溢出的时候,启用cookies来处理,可防范少量SYN攻击
net.ipv4.tcp_tw_reuse = 1
#开启tcp重用,允许将TIME—WAIT套接字重新用于新的TCP连接
net.ipv4.tcp_tw_recycle = 1
#开启TCP连接中的TIME-WAIT套接字的快速回收
net.ipv4.tcp_fin_timeout = 30
#修改tcpfin的默认timeout时间
  • /bin/sysctl -p 让参数生效

面试常见问题

1、为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

2、为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

3、为什么不能用两次握手进行连接?
若把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

4、如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接