计算机网络(六)网络编程
Socket 网络编程对应 TCP 三次握手的环节
- 服务端和客户端初始化
socket
,得到文件描述符; - 服务端首先会通过
bind
函数给 socket 绑定端口和IP地址,然后调用listen
函数,进行监听,然后调用accpet
函数等待客户端建立连接。 - 当客户端调用
connect
函数的时候,内核会随机生成初始化序号,放到 TCP 报文头部的序号字段中,同时把 SYN 标志设置为 1,这样就表示 SYN 报文。接着把这个 SYN 报文发送给服务端,之后客户端处于 SYN_SENT 状态。 - 服务端收到 SYN 报文后,首先服务端也会随机生成初始化序号,放到 TCP 报文头部的序号字段中,然后对客户端的初始化序号+1作为确认号,放到 TCP 报文头部的确认应答字段中,并将 SYN 和 ACK 标志设置为 1,这样就表示 SYN-ACK报文,后把该报文发给客户端,之后服务端处于 SYN_RCVD 状态。
- 客户端收到服务端SYN-ACK报文后,客户端会回一个 ACK 确认报文,该报文的确认号是服务端的初始化序号+1,并且 ACK 标志会设置为 1,之后客户端处于 ESTABLISHED 状态,这时候
connect
函数就返回了。 - 服务端收到 ACK 确认报文后,服务端也进入处于 ESTABLISHED 状态,这时候
accpet
函数就返回已建立连接的 socket 了,后续与客户端进行通信,就是对这个 socket 进行读写操作。
数据发送情况下断开:
- 客户端调用
write
写入数据;服务端调用read
读取数据; - 客户端断开连接时,会调用
close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
建立TCP连接,Socket 在 TCP 握手哪个阶段可以拿到连接?
需要完成三次握手后,服务端才能通过 accpet 函数得到已经建立 TCP 连接的 socket。
listen 的参数 backlog 意义是什么?
Linux内核中会维护两个队列:
- 半连接队列(SYN 队列):接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状态;
- 全连接队列(Accpet 队列):已完成 TCP 三次握手过程,处于 ESTABLISHED 状态;
int listen (int socketfd, int backlog)
参数一 socketfd 为 socketfd 文件描述符
参数二 backlog,这参数在历史版本有一定的变化 在早期 Linux 内核 backlog 是 SYN 队列大小,也就是未完成的队列大小。 在 Linux 内核 2.2 之后,backlog 变成 accept 队列,也就是已完成连接建立的队列长度,所以现在通常认为 backlog 是 accept 队列。 但是上限值是内核参数 somaxconn 的大小,也就说 accpet 队列长度 = min(backlog, somaxconn)。
没有 listen,能建立 TCP 连接吗?
可以的。
客户端是可以自己连自己的形成连接(TCP自连接),也可以两个客户端同时向对方发出请求建立连接(TCP同时打开),这两个情况都有个共同点,就是没有服务端参与,也就是没有 listen,就能 TCP 建立连接。
没有 accept,能建立 TCP 连接吗?
可以的。
accept 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accept 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。
当客户端多次发送连接,服务器没有accept,客户端会出现什么现象
会导致客户端无法和服务端建立 TCP 连接
因为如果服务端没有执行 accpet 的话,那么已经建立连接的 socket 会堆积到 TCP 全连接队列,如果 TCP 全连接队列占满了,服务端收到新的客户端 SYN 报文的时候,内核就会丢弃 SYN 报文。
四次挥手对应socket中通信的部分,其中哪些是用户调用,哪些是内核完成的?
第一次挥手和第三次挥手的 FIN 报文,都需要客户端或者服务端调用 close 函数触发的,第二次和四次挥手的 ACK 就是内核来完成。
如果客户端和服务端同时调用了close() 会发生什么?
由于 TCP 是双全工的协议,所以是会出现两方同时关闭连接的现象,也就是同时发送了 FIN 报文。
双方同时发送 FIN 报文,由于双方在等待 ACK 报文的过程中,都等来了 FIN 报文,这是一种新情况,所以连接会进入一种叫做 CLOSING 的新状态,它替代了 FIN_WAIT2 状态。接着,双方内核回复 ACK 确认对方发送通道的关闭后,进入 TIME_WAIT 状态,等待 2MSL 的时间后,连接自动关闭。
怎么知道客户端关闭连接?
如果客户端正常断开连接的情况下,会发起了第一次挥手 FIN 报文,服务端收到后,内核会插入一个文件结束符 EOF 到接收缓冲区中,应用程序可以通过 read 调用来感知这个 FIN 报文,如果 read 返回 0 了,就代表客户端关闭连接了。
如果客户端突然宕机了,服务端无感知客户端已经不存在了,如果接下来服务端不会在发送任何数据的话,超过一段时间后, TCP 就会开启保活机制,来探测客户端是否还存活,如果最终判断到客户端不存在了,服务端这边也会自动释放连接。