前言
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于流的通信协议。它是互联网协议栈(TCP/IP)中的核心协议之一,主要用于保证在计算机网络中可靠地传输数据。
TCP通信的基本特点
面向连接:在发送数据之前,TCP要求通信双方(客户端和服务器)首先建立一个连接,这个过程被称为“三次握手”。连接建立后,数据才可以传输;数据传输完成后,需要释放连接(通过“四次挥手”关闭连接)。
可靠传输:TCP保证数据包的正确传输。通过序列号和确认号的机制,TCP能够检测丢包、乱序、重复等问题,并通过重传机制进行纠正,从而确保数据的完整性和顺序性。
基于流:TCP传输的数据没有消息边界,而是一个连续的数据流。数据可以按照任意大小进行发送和接收,应用层必须根据协议或约定来解析数据边界。
本小节我们先来实现TCP通信,然后再来细分TCP通信当中的一些细节问题,关于tcp的三次握手和四次挥手看这篇文章:http://t.csdnimg.cn/WWgfZ
一、实现TCP通信
服务器代码:
//实现TCP服务器文件
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/ip.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
//1、创建套接字
int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd<0)
{
printf("创建失败\n");
return -1;
}
//2、绑定套接字
//填写自己的地址信息,不是必要的
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(8888);
serveraddr.sin_addr.s_addr=inet_addr("192.168.124.29");
int bind_ret=bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(bind_ret<0)
{
printf("绑定失败\n");
return -1;
}
//3、建立连接connect
//监听套接字
int listen_ret=listen(listenfd,10);
if(listen_ret<0)
{
perror("listen failed:");
return -1;
}
//建立连接accept
int serverfd=accept(listenfd,NULL,NULL);
if(serverfd<0)
{
perror("accept failed:");
return -1;
}
while(1)
{
//接收数据
char buf[100];
int ret=recv(serverfd,buf,100,0);
//读取数据发送回去
buf[ret]='\0';
int send_ret=send(serverfd,buf,strlen(buf),0);
if(send_ret<0)
{
perror("send failed:");
return -1;
}
if(send_ret==0)
break;
}
return 0;
}
然后我们使用网络调试器连接服务器:
网络调试器下载地址:通过网盘分享的文件:scomm.exe
链接: https://pan.baidu.com/s/1OkiZLT_CeoryEZepaOSGqQ 提取码: 8a85
如果出现绑定失败如下图:
不用紧张,我们下载号网络调试器之后双击运行;
连接完成之后,在调试器中发送你想要发送的内容,服务器接收到以后,会直接发送到客户端,也就是网络调试器中:
这样我们就完成了一个简单的TCP通信的实现过程
二、通信原理 (网路传输的封包与拆包)
数据在通信过程中的传输我们可以这样来看:
数据经过多次的封装,然后再发送给服务器,再每一层的封装过程中,都会加入一个协议,这个协议帮助数据完整的传输(也就是在我们发送数据的时候加入了网络层-IP头(协议)、传输层-TCP头(TCP协议),网络接口层-帧头(MAC地址)这个就是封包过程,接收端做拆包过程)。下面我们就以TCP传输作为例子。
首先,在客户应用当中我们输入了数据,如上图中的流程图一样,在传输层加入了TCP头,在网络层加入了IP头,在网络接口层加入了MAC帧头,我们仔细来看这些头里面有什么数据。
三、通信过程中的头
上面我们提到了每层中都有协议,那么具体每个协议里面都有什么内容数据呢,本小节我们就来看看里面有什么数据,这里就要用到一个抓包软件wireshark,像自己抓包的小伙伴可以自己下载,
首先,我们打开wireshark这个软件
点击WIAN进入抓包
当我们实现上面TCP服务器的时候,会出现建立连接的过程
1.MAC帧
看图中左下角,我们打开第二行是内核空间封装的MAC帧头地址
里面的信息如下:目标MAC地址(Destination)、源MAC地址(Source),type类型,type后面是十六进制的数,如图中是0X0800则表示只接收本机MAC地址的IPV4类型的数据帧
2. IP头
点击第三行中的数据,这是我们的IP头信息,在下图中可以看到我们使用的IPV4的版本,如下图左下角部分:
IPv4头部的标准长度为20字节,但它可以通过选项字段增加到60字节。以下是IPv4头的每个字段及其作用:
版本(Version):4位
- 该字段指示IP协议的版本。对于IPv4,它的值是4;对于IPv6,它的值是6。这个字段帮助网络设备识别和处理不同版本的IP包。
首部长度(IHL,Internet Header Length):4位
- 该字段表示IP头的长度,以32位字为单位。最小值是5(即20字节),如果有选项字段,它的值会更大。此字段用于确定IP头部的结束位置。
服务类型(Type of Service, ToS):8位
- 该字段用于指示数据包的优先级和服务质量要求,包括延迟、吞吐量和可靠性等。它现在通常被称为“Differentiated Services Code Point (DSCP)”和“Explicit Congestion Notification (ECN)”字段,用于网络流量的优先级和拥塞通知。
总长度(Total Length):16位
- 该字段表示整个IP数据包的长度,包括头部和数据部分,以字节为单位。最大值为65,535字节。这个字段用于接收端计算数据包的总长度并进行正确的解析。
标识(Identification):16位
- 该字段用于唯一标识每一个IP数据包,用于分片和重组。当一个大的数据包被分片时,每个片段都有相同的标识,以便接收端能够将这些片段重新组装成完整的数据包。
标志(Flags):3位
- 该字段用于控制和标识数据包的分片情况:
- 第一位(保留,Reserved):应为0,未来可能用于扩展。
- 第二位(Don't Fragment,DF):如果设置为1,表示数据包不允许分片。
- 第三位(More Fragments,MF):如果设置为1,表示数据包有更多的分片。
片偏移(Fragment Offset):13位
- 该字段表示数据包中片段的偏移量,以8字节为单位。它用于数据包的重组,指示每个片段在原始数据包中的位置。
生存时间(TTL, Time to Live):8位
- 该字段用于防止数据包在网络中无限循环。每经过一个路由器或跳数,TTL值减1。当TTL值减到0时,数据包被丢弃,并且通常会发送一个“时间超时”(Time Exceeded)消息回源地址。
协议(Protocol):8位
- 该字段指示IP数据包中的数据部分使用的传输层协议,如TCP(值为6)、UDP(值为17)等。它帮助接收方识别数据包的负载协议。
头部校验和(Header Checksum):16位
- 该字段用于检查IP头的完整性。它包含IP头部的校验和,接收方使用这个值来检测IP头部是否在传输过程中发生了错误。如果校验和不匹配,数据包会被丢弃。
源IP地址(Source Address):32位
- 该字段包含数据包发送方的IP地址,用于在网络中标识数据包的来源。
目的IP地址(Destination Address):32位
- 该字段包含数据包接收方的IP地址,用于在网络中标识数据包的目标。
选项(Options):0-40字节(可选)
- 这个字段是可选的,可以包含不同的网络控制信息,比如时间戳、安全选项等。如果没有使用选项字段,它的长度为0。选项字段的存在可以影响数据包的处理方式,但大多数应用和协议使用默认的头部设置而不添加选项。
填充(Padding):0-3字节
- 这个字段用于确保IP头部长度为32位的倍数。它用于对齐,以确保头部的总长度是4字节的整数倍。
3.TCP头
TCP头部是TCP协议中的重要部分,它负责确保数据的可靠传输。TCP头部包含了许多控制信息,用于管理连接、数据流和错误检测。
同样,我们点击第四行,这里面包含了TCP头部的信息,开始部分是我们的源端口号和目的端口号,在左下角部分
TCP头部格式
TCP头部的标准长度为20字节,但可以通过选项字段扩展。以下是TCP头部的每个字段及其作用:
源端口(Source Port):16位
- 该字段表示发送方的端口号。端口号用于在主机上区分不同的应用程序或服务。
目的端口(Destination Port):16位
- 该字段表示接收方的端口号,指明数据包应送达的具体应用程序或服务。
序列号(Sequence Number):32位
- 该字段用于标识发送的数据字节流中的位置。对于每个数据包,序列号帮助接收方按正确顺序重新组装数据。如果连接是新的,它表示第一个字节的序列号;如果是后续的数据包,它表示数据的字节位置。
确认号(Acknowledgment Number):32位
- 该字段用于确认已接收到的数据字节的序列号。确认号表示接收方期望接收的下一个字节的序列号。如果确认号为X,则表示接收方已成功接收序列号小于X的数据。
数据偏移(Data Offset):4位
- 该字段表示TCP头部的长度,以32位字为单位。它指示数据部分的起始位置,帮助接收方定位数据部分的开始。
保留(Reserved):3位
- 该字段保留供将来使用,当前应设置为0。
控制位(Flags):9位
- 该字段包含各种控制标志,用于管理TCP连接的状态:
- URG(Urgent Pointer):如果设置为1,表示数据包包含紧急数据。
- ACK(Acknowledgment):如果设置为1,表示确认号字段有效。
- PSH(Push):如果设置为1,表示接收方应立即将数据传递给应用层,而不是缓冲。
- RST(Reset):如果设置为1,表示强制重置连接。
- SYN(Synchronize):如果设置为1,表示请求建立连接,用于三次握手过程。
- FIN(Finish):如果设置为1,表示数据传输结束,请求关闭连接。
窗口大小(Window Size):16位
- 该字段用于流量控制,指示接收方当前的接收窗口大小,表示可以接收的最大字节数。它帮助发送方控制发送速率,以避免接收方缓存溢出。
校验和(Checksum):16位
- 该字段用于检查TCP头部和数据部分的完整性。接收方计算和比对校验和,以检测在传输过程中是否发生了错误。如果校验和不匹配,数据包会被丢弃。
紧急指针(Urgent Pointer):16位
- 该字段在URG标志位为1时有效,指示紧急数据的结束位置。接收方应优先处理这些紧急数据。
选项(Options):0-40字节(可选)
- 该字段用于提供附加的控制信息,如最大报文段长度(MSS)、时间戳、窗口缩放等。选项字段可以扩展TCP的功能,但不是所有的数据包都有选项字段。
填充(Padding):0-3字节
- 这个字段用于确保TCP头部长度为32位的倍数。它用于对齐,以确保头部的总长度是4字节的整数倍。
数据(Data):变长
- 这是TCP头部之后的数据部分,包括实际传输的应用数据。数据部分的长度由数据偏移字段指示。
TCP头部的应用
连接管理:通过SYN和ACK标志,TCP能够管理连接的建立和关闭。SYN用于建立连接,ACK用于确认数据包的接收。
数据传输:序列号和确认号用于跟踪数据的发送和接收,确保数据包按照正确的顺序到达,并进行重传以纠正丢包。
流量控制:通过窗口大小字段,TCP可以动态调整数据的发送速率,防止接收方缓存溢出。
错误检测:校验和字段用于确保数据的完整性,检测和修复传输中的错误。
紧急数据处理:紧急指针字段用于处理优先级数据,确保紧急数据得到及时处理。
选项扩展:选项字段允许在TCP头部添加附加功能,如优化传输性能和支持更多的网络功能。
4.UDP头
UDP(User Datagram Protocol,用户数据报协议)是一个简单的、无连接的传输层协议。与TCP不同,UDP不提供连接管理、流量控制和错误恢复机制,它更注重于提供快速的、低开销的数据传输。UDP头部设计简单且直接,用于快速传输数据。如下图:
udp的头很简单
UDP头部格式
UDP头部固定为8字节(64位),每个字段的作用如下:
源端口(Source Port):16位
- 该字段表示发送方的端口号。端口号用于在主机上标识不同的应用程序或服务。
目的端口(Destination Port):16位
- 该字段表示接收方的端口号,指示数据包应送达的具体应用程序或服务。
长度(Length):16位
- 该字段表示UDP头部和数据部分的总长度,以字节为单位。最小值是8字节(仅头部),最大值为65,535字节(包括数据)。这个字段用于接收方计算UDP数据包的总长度,确保完整接收。
校验和(Checksum):16位
- 该字段用于检查UDP头部和数据部分的完整性。校验和用于检测在传输过程中是否发生了错误。如果校验和不匹配,数据包可能会被丢弃(具体取决于实现和配置)。校验和字段是可选的,但建议使用,以提高数据传输的可靠性。
从这里也可以看出UDP通信和TCP通信的区别,我们经常说tcp通信可靠也是因为TCP头中包含数据很多,便于数据传输的完整性,后面我会单独出一篇来介绍
总结
- MAC帧(以太网帧):以太网帧是数据链路层的一个重要组成部分,用于在局域网(LAN)中传输数据。
- IPv4头部:包含用于路由和分片的各种信息,如源和目的IP地址、TTL、协议类型等。
- TCP头部:提供了连接管理、数据流控制和错误检测的功能,适用于需要可靠数据传输的应用。
- UDP头部:设计简洁,不提供连接管理和流量控制,适用于需要快速传输的应用。