协议
协议:从应用角度讲,协议就是一组规则,用于数据的传输和解释
典型协议
应用层:HTTP、FTP、NFS、SSH、telnet
传输层:TCP、UDP
网络层:IP、ICMP、IGMP
链路层:以太网帧协议、ARP
网络应用程序设计模式
c/s模式:
客户端服务器端架构 client/server
- 优点:
- 可以缓存数据,提高传输效率
- 协议选择灵活,可定制、剪裁
- 缺点:
- 安全性较差
- 不支持跨平台
- 开发工作量大
b/s模式:
浏览器服务器架构 browser/server
- 优点:
- 安全性好
- 支持跨平台
- 开发工作量较小
- 缺点:
- 数据无法缓存
- 只能采用固定协议
分层模型
OSI七层模型
物、数、网、传、会、表、应
TCP/IP四层模型
链路层(网络接口层)、网络层、传输层、应用层
通信过程
数据没有封装之前,是不能在网络中传递的
协议格式
MAC地址:唯一标识一台主机(网卡)
IP地址:可以在网络环境中,唯一标识一台主机
端口号:可以在网络环境中的一台主机上,唯一标识一个进程
IP地址+端口号:可以在网络环境中,唯一标识一个进程
数据包封装
以太网帧格式
ARP协议:根据IP地址获取MAC地址(也叫以太网地址)
以太网帧协议:根据MAC地址,完成数据包传输
ARP数据报格式
ARP请求/应答:属于以太网帧格式中的一种(以太网帧首部类型为0806)
IP段格式
版本: IPv4(4字节)、IPv6(16字节)
TTL:即time to live,设置数据包在路由节点中的跳转上限。每经过一个路由节点,该值-1,减为0的路由,会将该数据包丢弃
源IP: 32位,即4字节
目的IP:4字节
生活中见到的192.159.32.68是点分十进制的IP地址,以及转化为string类型了,本质上是32位二进制
UDP数据报格式
源端口号:16位,那么最大是2^16 = 65536
目的端口号:16位
TCP数据报格式
六个标志位:
- SYN:即synchronize(同步),用于请求建立连接
- ACK:即acknowledgment(确认),用于确认已收到数据或请求,表示1021之前的数据都收到了
- FIN:即finish(结束),用于请求关闭连接
TCP协议
TCP通信时序(三次握手和四次挥手)
三次握手在内核空间完成(不用手动操作),在用户空间层面上的体现是accept函数(服务器)和connect函数(客户端)成功执行并返回
滑动窗口(TCP流量控制)
发送给连接对方本端的缓冲区大小(实时),以提醒对方不要发来多于目前缓冲区大小的数据,避免数据丢失
※TCP状态转换
- 主动发起连接请求端:
CLOSED
→发送SYN→SYN_SENT
→接收SYN、ACK→SYN_SENT
→发送ACK→ESTABLISHED
(数据通信态) - 主动关闭连接请求端:
ESTABLISHED
→发送FIN→FIN_WAIT_1
→接收ACK→FIN_WAIT_
2(半关闭)→接收对端发送的FIN→FIN_WAIT_
2→回发ACK→TIME_WAIT
→等待2MSL时长(120s)→CLOSED
- 被动接收连接请求端:
CLOSED
→LISTEN
→接收SYN→LISTEN
→发送SYN、ACK→SYN_RCVD
→接收ACK→ESTABLISHED
- 被动关闭连接请求端:
ESTABLISHED
→接收FIN→ESTABLISHED
→发送ACK→CLOSE_WAIT
(此时对端处于半关闭状态)→发送FIN→LAST_ACK
→接收ACK→CLOSED
ps:可以通过命令netstat -apn | grep 端口号
查看服务器和客户端的当前状态
重点记忆:
FIN_WAIT_2
:主动关闭连接的一方,发出FIN收到ACK后进入该状态(半关闭)CLOSE_WAIT
:被动关闭连接的一方,发送ACK后进入该状态,此时对端处于半关闭状态TIME_WAIT
:主动关闭连接的一方,回发ACK后进入该状态,然后等待2MSL时长(120s)
半关闭
即FIN_WAIT_2
状态
通信双方中,只有一端关闭通信 (但是连接还在,还能接收标志位)
导致半关闭的函数:
- close(cfd);
- shutdown(int fd, int how);
- fd:文件描述符
- how:
- SHUT_RD 关读端
- SHUT_WR 关写端
- SHUT_RDWR 关读写
ps:shutdown()
在关闭多个文件描述符应用的文件时,采用全关闭方法(即把所有重定向指向该文件的文件描述符全部关闭),而close()
只关闭一个文件描述符
2MSL
出现在【主动关闭连接请求端】对应TIME_WAIT
状态
保证最后一个ACK能成功被对端接收
在2MSL等待期间,如果对端没收到我发的ACK,对端会再次发送FIN请求,这样我就知道你还没收到,我会再发ACK,等待2MSL时长,如果在这段时间里你没有应答了,就说明你大大概率收到我的ACK了,四次挥手结束,我总不能一直等下去吧
端口复用
目的:如果在某些情况下服务器必须主动断开连接(一般情况下是客户端主动),那么服务器在TIME_WAIT
状态后会等待2MSL时长,由于服务器所用的端口必须是固定的(否则客户端去哪里找你)且此时在2MSL时长中还在被占用,这时候重启服务器就会失败,为解决这种情况可以在bind之前设置端口复用
int opt = 1; //设置端口复用
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)); //函数原型太复杂,暂不深究