网络常用命令:
netstat命令:
netstat命令用来查看网络状态。
语法:netstat [选项]
选项 | 功能 |
---|---|
-a | 显示所有连接中的socket |
-u | 显示UDP传输协议的连接状态 |
-t | 显示TCP传输协议的连接状态 |
-p | 显示正在使用socket的程序识别码和程序名称 |
-l | 显示监听中的服务器socket |
-n | 直接使用IP地址,而不通过域名服务器进行转换 |
-c | 持续列出网络状态 |
pidof命令:
语法:pidof [进程名]
功能:通过进程名查看进程ID。
TCP格式:
我们在讲TCP可靠性的原理之前,先来认识一下TCP协议的格式,如下:
- 16位的源、目的端口号:源端口号用于指定发送方的进程,目的端口号用于指定接收方的进程。
- 32位序号:用于标识一个特定的TCP包。
- 32位确认号:确认应答机制中,接收方已收到的最大序号的下一个序号。
- 4位数据偏移/首部长度:由于首部可能包含可选项内容,因此TCP报头的长度是不确定的,报头不包含任何可选字段时长度为20字节,4位首部长度可表示的最大值为15,单位为4字节,所以总共可表示的最大字节数为15x4=60字节。首部长度也叫数据偏移是因为此字段指示了数据部分距离报文开始位置的偏移。
- 6位标志位:
- URG:紧急指针是否有效。
- ACK:确认号是否有效。
- PSH:提示接收端程序立即从TCP缓冲区将数据拿走。
- RST:对方要求重新建立连接,含有RST标识的称为复位报文。
- SYN:请求建立连接,含有SYN标识的称为同步报文。
- FIN:通知对端,本端要关闭了,含有FIN标识的称为结束报文。
- 16位窗口:用于流量控制。
- 16位检验和:包含首部与数据部分的CRC校验,由接收方填充。
- 16位紧急指针:标识那部分数据是紧急数据。
TIME_WAIT状态:
TCP要经过三次握手建立连接、四次挥手释放连接。
服务器端状态变化:
- CLOSED->LISTEN:服务器端调用listen()函数后,进入LISTEN状态等待客户端发起连接。
- LISTEN->SYN_RCVD:服务器端接收到客户端发送的SYN报文(同步报文),就将该连接放入内核的等待队列(Ack队列)中,并发送SYN+ACK报文。
- SYN_RCVD->ESTABLISHED:服务器端收到客户端的确认报文并且将连接放入Complete队列后进入ESTABLISHED状态,等待accept()函数返回,就可以收发数据了。
- ESTABLISHED->CLOSE_WAIT:客户端主动关闭连接(调用close()),服务器端收到FIN报文(结束报文),进入CLOSE_WAIT状态并发送确认报文。
- CLOSE_WAIT->LAST_ACK:当服务器端进入CLOSE_WAIT状态说明服务器端准备关闭连接,处理完之前的数据之后,服务器端调用close()函数关闭连接,并向客户端发送FIN报文,同时进入LAST_ACK状态等待客户端返回最后一个确认报文,此确认报文表示客户端收到服务器端发送的FIN报文。
- LAST_ACK->CLOSED:服务器端收到最后一个确认报文后,进入CLOSED状态彻底关闭连接。
客户端状态变化:
- CLOSED->SYN_SENT:客户端调用connect()函数,进入SYN_SENT状态,并向服务器端发送SYN报文(同步报文)。
- SYN_SENT->ESTABLISHED:客户端收到服务器端返回的对FIN报文的响应报文进入ESTABLISHED状态并向服务器端发送确认报文,connect()返回,客户端读写数据。
- ESTABLISHED->FIN_WAIT1:客户端主动调用close()断开连接,进入FIN_WAIT1状态并发送FIN报文(结束报文)。
- FIN_WAIT1->FIN_WAIT2:客户端收到服务器端对FIN报文的确认报文,进入FIN_WAIT2状态,等待服务器端的结束报文。
- FIN_WAIT2->TIME_WAIT:客户端收到服务器端的FIN报文(结束报文),进入TIME_WAIT状态并发送最后的ACK报文(响应报文)。
- TIME_WAIT->CLOSED:客户端要等待一个2MSL(报文最大生存时间)时间,才会进入CLOSED状态。
在测试网络程序时,我们会发现用Ctrl+c将程序结束后,再次立即重新运行时,会报出下面的错误:
[hello]$ ./server 0 9090
bind: Address already in use
[hello]$
但是,经过一段时间的等待再次运行又能正常运行,这到底是什么错误呢?
既然是bind()函数报的出“地址已经使用”的错误,那么我们就用netstat -anp |grep 9090
命令来看看相应的端口号的状态。
[hello]# netstat -anp |grep 9090
tcp 0 0 172.17.165.235:9090 117.136.86.132:53910 TIME_WAIT -
tcp 0 0 172.17.165.235:9090 117.136.86.132:53909 TIME_WAIT -
[hello]#
我们会发现9090端口的状态是TIME_WAIT状态,想必再结合过一段时间便可正常运行的现象,原因便出在TIME_WAIT状态身上。
原来,这种现象是因为,虽然我们终止了应用程序,但TCP协议层的连接还没有完全断开连接,还处于TIME_WAIT状态,所以不能再次绑定相同的端口号。
- TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待2MSL时间才能返回CLOSED状态。
- MSL在各操作系统中实现不同,在CentOS7中可以通过
cat /proc/sys/net/ipv4/tcp_fin_timeout
查看。
TIME_WAIT解决方法:
使用setsockopt()函数设置socket描述符的SO_REUSEADDR选项为1,便可重复绑定相同的IP地址和端口号,SO_REUSEADDR选项为1表示:当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,此时你启动的程序的socket2可占用该地址和端口。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
参数:
- sockfd:要设置的套接字。
- level:被设置选项的级别,SOL_SOCKET为套接字级别。
- optname:要设置的选项。
- optval:指向要设置的值。
- optlen:optval指向值的长度。
实例:
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
下面我们来看看TCP的可靠性都是通过那些机制保证的。
TCP的可靠性原理:
校验和:
在TCP报文首部中有一个校验和字段,采用CRC校验(包含首部与数据部分的校验),由发送方填充,接收方在接受报文的时候检验校验和字段,若不一致则认为数据出错丢弃,校验和可以保证在接收端接收到的数据与发送端发送的数据的一致性,从而避免数据传输错误的可能。
序列号:
在接收端接收数据时,会根据首部中的序列号将接收到的数据包在接收缓冲区中排序,TCP报文首部中的序列号字段可以保证TCP报文按序到达接收端,从而使接收端能正确解析到达的多个数据包。
确认应答:
确认应答机制是指,在接受方接收到一个数据包时将会给发送方发送一个确认报文,在确认报文中指明了下一个要接收的数据包的序列号,确认应答机制保证了接收方能接收到发送方发送的每一个报文,避面了在传输过程中的丢包问题。
超时重传:
超时重传机制是指,在一定的时间后发送方还没有收到接收方的确认报文,将会重新发送这个报文,超时重传和确认应答机制共同保证了传输过程中丢包的问题。
连接管理:
连接管理包括三次握手、四次挥手机制,保证了TCP通讯双方正确的建立连接与关闭连接。