网络编程 API
组成
服务端API | 客户端API |
socket() |
socket() |
bind() |
bind() [optional] |
listen() |
|
accept() |
connect() |
recv() |
|
send() |
send() |
close() |
recv() |
close() |
|
shutdown() |
三次握手服务器和客户端分别是发生在哪个函数
- 服务器调用
listen
使服务器处于LISTEN
状态,等待客户端请求连接 - 首先客户端调用
connect
发起第一次SYN
包,然后处于**SYN_SENT
状态**,此时服务端会将这个连接放进半连接队列 - 服务端收到
SYN
包以后会发送给客户端一个ACK|SYN
包,此时服务端状态转移为SYN_RECV
- 客户端接收到服务端发来的
ACK|SYN
包进行解析后会在半连接队列中查找携带这个信息的节点是否存在,如果存在则move
出来加入全连接队列中,并且发送ACK
包给服务端,此时客户端状态转移为ESTABLISHED
- 服务端收到客户端的
ack
后也状态转移为ESTABLISHED
知识点:
半连接队列与全连接队列
- 都是在服务端那里存在的队列
backlog
参数的含义
- 三种说法都存在
- 半连接队列大小
- 两个队列大小的和
- 全连接队列大小
- 本机上
man listen
查看defines the maximum length to which the pending connections for sockfd for sockfd may grow
代表全连接队列的大小
ddos
攻击
- 客户端只发第一次握手,不会返回
ACK
,使半连接队列溢出
双方都发起第一次握手可以吗
- 不可以,会导致连接无法正常建立
数据传输阶段
send
返回整数不一定代表发送成功
send
只是将数据拷贝到了内核,之后多久发送是协议栈规定的,自己不知道
为什么会发生发送不成功?
- 据包在传输过程中很复杂,可能对方网络很差,在某一个路由器中丢包了,数据包总是根据目的ip、port来寻找
传输过程中的粘包分包问题
客户端调用send
,send
只将数据拷贝到协议栈里面,其他什么都不做
如何做好分包粘包
- 多次
send
会发生粘包和合包的问题
- 解决
- 应用层协议头前面加
packetlen
- 为每一个包加上分隔符
\r\n
- 这两种做法有什么区别
// 先读长度,再读数据 read(tcphdr, 2); read(tcphdr->length); //or while(count <tcphdr->length){ size = read(tcphdr->lenghth - count); count += size; }
// 检测内容 read(buffer, 1024); buffer[idx] = "\r\n"; pkt = &buffer[idx + 2]; // 为了后续合包
网线断了立马重启,tcp是如何检测到的
- 通过设计
->
心跳包来检测
网络断开连接
不分客户端或者服务器,只分主动或者被动
- 首先调用
close
的端会将最后一次包的FIN
位置为1
,并且处于状态为FIN_WAIT1
- 对端收到带有
FIN
的包后会返回给发送端一个ACK
包,这个是立即发送的,代表我知道你不给我发数据了,之后对端状态转移为CLOSE_WAIT
- 发送端接收到后状态转移为
FIN_WAIT2
- 之后对端在发送
FIN
包之前的这一段时间,它还可以给发送端发数据,当对端处理好所有数据后会发送最后的FIN
包,此时状态会转移为LAST_ACK
- 发送端接收到对端发送的
FIN
包之后会发送一个ACK
包然后处于**TIME_WAIT
状态**,过了这段时间后就处于**CLOSE
状态** - 对端接收到
ACK
后连接断开,处于CLOSE
状态
一些面试题:
- 如果出现大量
close_wait
该怎么办(这种现象是因为recv
返回0
后到调用close
时间太长)
- 把业务数据释放与网路层的(
recv
和close
)做一个分离,把他做成异步的,就是将释放资源客户端相关做成异步,这样一来recv==0
后就可以立即close(fd)
了
- 会出现双方同时调用
close
吗?引入closing
- 以某一端为例,先发
fin
,进入fin_wait1
,然后收到对端发送的fin
包而不是ack
,则为两端同时关闭,直接进入closing
状态
- 为什么会有
time_wait
- 如果
laskack
在传输过程中丢了,则对端会重发fin
,如果没有time_wait
则对应的端口会立即被释放出来,如果此时有一个应用程序连接进来刚好用到了该端口,则会直接收到fin
包从而进入close_wait
状态从而直接进入四次挥手
- 为了避免最后一次
ack
丢失
简单总结各个函数的作用
socket
- 文件系统分配
fd
,且分配一个tcb
bind
- 绑定本地的
ip
、端口
listen
- 将
fd
置为listen
状态
accept
- 从全连接队列中取出一个
tcb
(TCP control block
) - 并分配一个
fd
recv
- 在对应的
fd
里面,将对应的readbuffer
读出来
send
- 在
fd
对应的tcb
里面将数据拷贝到sendbuffer
里面
close
- 将
sendbuffer
里面的最后一个包的fin位置为1 - 回收
fd
客户端
bind
- 可选,如果不调用则随机端口,调用则指定端口
connect
- 准备一个
syn
包给服务端发送过去 - 等待三次握手完成之后
connect
再返回 - 思考:这个函数有没有等待的动作,是不是个阻塞的,如果连不上,会阻塞吗
- 有等待的动作但不会阻塞,如果连不上则会被连接被拒绝