TCP协议:(传输控制协议,Transmission Control Protocol)
为应用层提供可靠的、面向连接的和基于流的服务。TCP协议使用超时重传、数据确认等方式来确保数据包被正确的放至目的端口,因此TCP服务是可靠的。使用TCP协议的通信双方必须先建立TCP连接,双方都必须为该连接分配必要的内核资源,以管理连接状态和连接上数据的传输。TCP连接是全双工的,即双方的数据读写可以通过一个连接进行。
TCP采用应答确认机制以及超时重传机制确保可靠传输,最后,因为TCP报文最终是以IP数据报发送的,而IP数据报到达接收端可能乱序、重复,所以TCP协议还会对接收到的TCP报文段重排、整理,在交付给应用层。
TCP头部结构:
4位头部长度:标识该TCP头部有多少个32bit字(4字节),TCP头部最长是60字节
6位标志位分别为:
- URG标志,标识紧急指针是否有效
- ACK标志,表示确认号是否有效,我们称携带ACK标志的TCP报文段为确认报文段
- PSH标志,提示接收端应用程序应该立即从TCP接收缓冲区中读取数据,为后续数据腾出空间
- RST标志,表示要求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段
- SYN标志,表示请求建立一个连接。我们称携带SYN标志的TCP报文段为同步报文段
- FIN标志,表示通知对方本端要关闭连接了。我们称携带FIN标志的TCP报文段为结束报文段
16位窗口大小:是TCP流量控制的一个手段。告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据这样对方就可以控制发送数据的速度
UDP协议:(用户数据报协议,User Datagram Protocol)
与TCP完全相反,它为应用层提供不可靠、无连接和基于数据报的服务。“不可靠”以为着UDP协议无法保证数据从发送端正确的传送到目的端,数据中途假如丢失,值简单通知应用程序发送失败。基于数据报服务意味着每个数据报都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性全部读出,否则数据将被截断。还需要自己处理数据确认、超时重传等逻辑。
UDP头部结构:
UDP非常适合广播或多播(目标是多个主机地址)的应用程序。
TCP编程流程:(参考书籍:《Linux高性能服务器》)
服务器端:
创建 int socket(int domain,int type,int pro);//失败返回-1 成功返回一个文件描述符
- domain------告诉系统使用哪个底层协议族,对TCP/IP协议族而言,该参数应该设置为PF_INET(用于IPv4)或PF_INET6(用于IPv6);对于UNIX本地域协议族而言,该参数应该设置为PF_UNIX
- type-------指定服务类型。服务类型主要有SOCK_STREAM服务(流式服务)和SOCK_UGRAM(数据报)服务。对于TCP/IP协议族而言,其值取流式服务表示传输层使用TCP协议,数据报表示传输层使用UDP服务
非阻塞、阻塞概念见上一篇博客同步、异步、阻塞与非阻塞 (有很多书中截图qaq,我太懒了o(╥﹏╥)o)
- Protocol--------几乎所有情况下都为0 ,表示使用默认协议
命名 int bind(int sockfd,struct sockaddr* saddr,int size);//-1/0 错误原由:IP错误 或者 端口号被占用
- size之处该socket地址的长度
监听 int listen(int sockfd,int size);//启动监听 不阻塞
- size典型值为5,
q:常有面试题问道listen()第二个参数是什么?-------------a:listen()方法是创建监听队列,一般有两个,一个是未完成三次握手的,一个是已完成三次握手的。Linux中自2.2版本之后,第二个参数表示已完成三次握手的队列长度。在一些unix系统中第二个参数表示未完成和已完成两个队列长度之和
while(1){ //循环完成多次/接受多个客户端连接
接受连接 int accept(int sockfd,struct sockaddr *caddr,int *len);//出错返回-1 成功返回>=0的文件描述符c
- caddr用来获取被接受连接的源端socket地址,该socket地址的长度由addrlen参数指出
while{ //while循环完成与一个客户端的多次交互
写 int recv(int c,void *buff,int size,int flag); //recv成功返回实际读取到的数据的长度 失败-1 可能为0 表示对方关闭连接 只有accept/recv有可能阻塞
读 int send(int c;void *buff,int datalen,int flag);//成功返回实际写入的数据的长度失败-1 flags通常都为0
}
int close(int fd);
}
客户端:
int socket(int domain,int type,int pro);
发起连接 int connect(int sockfd,struct sockaddr*saddr,int len);//成功返回0 失败-1 常见错误原由:目标端口不存在连接被拒绝 或 连接超时
- saddr是服务器监听的socket地址,len则指定这个地址的长度
while(){ // while循环完成多次与服务器的多次交互
int send(int sockfd,void *buff,int len,int flag);
int recv(int sockfd,void *buff,int size,int flag);
}
int close(int fd);
UDP编程流程:
服务端:
int socket(int domain,int type,int pro);
int bind(int sockfd,struct sockaddr* saddr,int size);
int recvfrom(int sockfd,void *buff,int size,int flag,struct sockaddr*caddr,int *addrlen);
无连接
int sendto(int sockfd,void *buff,int datalen,int flag,struct sockaddr* caddr,int addrlen);
int close(int sockfd);
客户端:
int socket(int domain,int type,int pro);
int recvfrom(int sockfd,void *buff,int size,int flag, struct sockaddr*caddr,int *addrlen);
int sendto(int sockfd,void *buff,int datalen,int flag, struct sockaddr*caddr,int addrlen);
int close(int fd);
三次握手四次挥手:
- 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
- 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
- 第一次分手:主机1(可以是客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
- 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
- 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
- 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。
TCP状态转移图:(灰常重要的!)ps:《Linux高性能》的3.4节
常见的tcp面试题:
- 为什么是三次握手而不是两次握手?
如果最后的一个ACK,则服务器会不断超时重传ACK/SYN
会浪费服务器的资源
- 三次握手那个阶段容易出现攻击?
比较典型的是syn泛洪攻击,或syn溢出攻击
- TIME_WAIT状态存在的意义?
- 三次握手哪个阶段会出现异常?
第二阶段,服务器端口若没打开,会恢复RST复位报文,握手失败。另外listen()创建的监听队列达到上限也可能失败
- 如何查看TCP连接状态?
netstat 或 ss
- TCP为啥挥手要比握手多一次?
因为当处于LISTEN状态的服务器端收到来自客户端的SYN报文(客户端希望新建一个TCP连接)时,它可以把ACK(确认应答)和SYN(同步序号)放在同一个报文里来发送给客户端。但在关闭TCP连接时,当收到对方的FIN报文时,对方仅仅表示对方已经没有数据发送给你了,但是你自己可能还有数据需要发送给对方,则等你发送完剩余的数据给对方之后,再发送FIN报文给对方来表示你数据已经发送完毕,并请求关闭连接,所以通常情况下,这里的ACK报文和FIN报文都是分开发送的。
- TCP与UDP的区别?应用场景都有哪些?
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
- UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP对系统资源要求较多,UDP对系统资源要求较少。
- 若通信数据完整性需让位与通信实时性,则应该选用 TCP 协议(如文件传输、重要状态的更新等);反之,则使用 UDP 协议(如视频传输、实时通信等)。
- UDP:DNS SNMP
- TCP面向字节流,UTP面向数据包;
、