概述
1.什么是TCP传输协议
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。
额,总之是用来通信和传输数据的。
2.TCP和UDP协议
TCP和UDP协议是TCP/IP协议的核心。 TCP 传输协议:TCP 协议是一TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。
tcp可靠,udp不可靠。但是upd开销小,总之各有各的优缺点。
TCP报文格式
(源自百度百科)
UDP报文格式
(源自百度百科)
剖析
1.tcp的分包和粘包问题
1)如何处理tcp粘包
首先tcp协议收发数据包的特点是:1.先发先到,有序到达
2.进行可靠传输,保证端与端之间数据到达
这里我们假设MTU(最大传输单元)为1500字节,A端向B端发送4个大小不同的数据包,总共有2048字节数据,这时B端只需要两次即可接受完A端发送的数据,第一次接收1500字节,第二次接收548字节,这时候我们要如何区分出原来A端的数据包分组。
方法一:在每个数据包之前加上包长
一个数据包读两次数据,第一次读取包长度,第二次再读数据
while(1) { recv(fd, buffer, 2, 0); short len = ntohs(buffer); recv(fd, buffer, len, 0); }
方法二:由于tcp传输是有序的,因此可以在每个数据包之间加分隔符,通过分隔符判断数据包
2)如何处理tcp分包
处理方法其实与处理粘包类似,有细微差别
// mtu = 1500; while(1) { // 第一次读先读出数据包的长度 recv(fd, buffer, 2, 0); short length = ntohs(buffer); // 第二次读再读数据 int total = 0; do { int count = recv(fd, buffer, len, 0); total += count; // 累加每次读出的数据 } while(tota <= length); // 当读完了全部数据则退出循环 }
2.tcp如何确认发送成功了
注意:当应用层App调用send函数时,send返回成功值代表的是数据成功从应用层中拷贝到内核,而并不是从内核中成功发送。send失败通常是在快速的基站切换时,TCP发送失败,失败重传也发送不出去,或者是坐电梯没有信号时,send会返回-1。
只有当A端发出数据包后,B端回复了响应数据包,tcp才能确认发送成功了。发送出去的每个数据包都会收到ack确认。
3.tcp四次挥手时产生的大量的close_wait问题
tcp分为三个阶段:
1.建立连接
2.传输数据
3.断开连接
例如在某些实时通讯系统中,每当一个客户端断开连接时,会产生大量的close情况,但此时每一个客户端断开连接调用close()之前,每个用户都会有自己的临时数据,可能在调用close之前需要调用cleanup(user_informations),而调用cleanup可能会耗费较长时间,没有及时调用close而导致了大量close_wait问题。
/* 修改之前 ret = recv(); if(ret == 0) { cleanup(user_information); close(); } */ // 可以将cleanup做成异步的,放入任务队列,将cleanup做为回调函数 ret = recv(); if(ret == 0) { push_task(cleanup, user_information); close(); }
4.在QQ中使用的传输协议
为什么qq使用的是udp而非tcp?
历史背景原因:
在2000年左右时,还没有出现io多路复用,iocp等高并发技术的支持。使用tcp网络通信时,对应的每一个链接都会被分配一个fd,对于那时候还没有epoll,kqueue等技术,如此之多的fd该如何管理是一个巨大的问题,只能一个fd对应一个线程,每个fd创建一个线程循环读,当用户量大时会产生巨大的开销。而使用udp网络通信时,它只有一个fd就是recvfrom();只需要从端口每次循环的接收数据即可。当时的qq网络并发io技术无法支持使用tcp协议带来的开销,采用的是windows系统 +
upd