对于传输层的这两个重要的协议:TCP协议与UDP协议,今天我们将从以下分别其特点的三点开始分析。
一、基本区别
TCP(Tranfer Control Protocol):面向连接的,可靠的,字节流服务
UDP(User Datagram Protocol):无连接的,不可靠的,数据报服务
二、细致对比说明
- TCP:面向连接的
- 两端通讯过程之前,必须先建立连接;
- 连接一旦建立,两端交互过程都在这一连接上完成,无需重新建立连接,直到关闭连接为止;
- 通讯完成后,需要断开连接,以释放服务器的资源;
- 操作系统为我们维护连接,所以内核需要为连接分配相应的资源;
- 对于服务器而言,一个连接只能为一个客户端服务,从而造成服务器性能有待优化。
- TCP:数据传输的可靠性 (与协议控制有关,也就是依靠TCP协议报头中做控制)
1.数据是否能到达对端?
答:所有的数据都能到达对端
(依靠32位确认号,超时重传,滑动窗口,拥塞控制)
2.数据到达对端是否会乱序?
答:排序不乱序, 处理重复的报文段
(依靠TCP报头中的序号32位序号实现 )
3.数据是否被修改即失真
答:数据不出错,不仅校验TCP头部,还会校验数据部分
(依靠CRC 16位校验和 )
接下来带领大家看一下TCP的协议报头,了解一些以上内容的相关知识
32位序号:第一个报文段系统会生成一个随机值,后续报文段的序号是报文段携带的数据的第一个字节在整个字节流中的偏移量+生成的随机值;例如
- TCP 2000 30字节
- TCP 030 0字节
- TCP 2050 50字节
- TCP 2100 20字节
32位确认号:由接收数据的一方填充,接收到的报文段的序号值+1,;对接收到的报文段的确认;
4Head_len:确定TCP的报头最大值为15个 四字节,即60字节;
6位标示位:
URG标志,表示紧急指针,如有紧急需要传输的数据时使用。
ACK标志,表示确认号,我们称携带ACK标志的报文段为确认报文段。
PSH标志,表示接收端应该立即从缓冲区读走数据,为后续接收数据腾出空间。
RST标志,表示要求对方重新建立连接,我们称携带RST标志的报文段为复位报文段。
SYN标志,表示请求建立一个连接,我们称携带SYN标志的报文段为同步报文段。
FIN标志,表示通知对方本端要关闭了,我们称携带FIN标志的报文段为结束报文段。
16位窗口大小:是TCP流量控制的一个手段,窗口是指接收通告窗口(RWND),它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,对方就可以控制发送数据的速度。
16位校验和:发送方填充 接收方通过CRC算法根据校验和对数据进行校验(TCP)重可靠传输的重要保障
16位紧急指针:是一个正的偏移量。 确切的说,这个字段的紧急指针相对当前序号的偏移。它是发送端向接收端发送紧急数据的方法。
- TCP:字节流服务(抽刀断水水更流)
1.send的次数和接收方recv的次数没有必然联系(受当前的程序和网络环境影响)
底层数据发送或者接收数据时,数据有可能被分开或者合并。
2.如果发送方发送一次数据,接收方一次未接收完成,数据不会丢失。
UDP:无连接的
1.是一种无连接的协议;
2.每个数据包都是一个独立的信息,包括完整的源地址或目的地址;
3.它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
UDP:数据传输的不可靠
1.数据是否能到达对端;
2.数据到达对端是否会乱序;
3.数据是否被修改即失真。
答:上述三个问题都是不确定的,因为没有UDP保障。
UDP的报头是:
它和TCP的一些含义差不多,在此就不做什么说明了。特别注意:UDP的头部8字节,TCP的头部是20字节。
UDP:数据报服务
- sendto的次数和recvfrom的次数相等;
- 如果recvfrom一次未将数据读取完,则剩余的丢弃。
接着我给大家用两张图展示下TCP连接和断开的过程,也就是我们常说的三次握手和四次挥手。
三、TCP的三次握手和四次挥手
TCP建立连接过程三次握手
TCP断开连接过程四次挥手
TIME_WAIT状态的含义: (与CLOSE_WAIT 会混淆)
- 保证迟来的数据能被识别并丢弃
- 保证可靠的终止TCP连接
ps:这也是我们需要记住并清楚的一点!!!
四、TCP的代码实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <string.h>
int listenfd = -1; //全局变量
void sigfun(int sign)
{
close(listenfd);
exit(0);
}
int main()
{
signal(SIGINT,sigfun);
listenfd = socket(AF_INET,SOCK_STREAM,0);//协议簇 TCP协议
assert(listenfd != -1);
struct sockaddr_in ser,cli;
memset(&ser,0,sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_addr.s_addr = inet_addr("127.0.0.1"); //IP地址
ser.sin_port = htons(6000);
int res = bind(listenfd,(struct sockaddr *)&ser,sizeof(ser));
assert(res != -1);//绑定失败 1.IP地址不对 2.端口号被占用或者没有权限使用
listen(listenfd,5); //size = 5 内核维护的已经完成链接客户端的文件描述符个数(6)实际会加一
while(1)//服务器接收不同客户端链接的循环
{
int clilen = sizeof(cli);
int c = accept(listenfd,(struct sockaddr*)&cli,&clilen);//记录客户端的地址信息
assert(c != -1);//与一个客户端交互的循环
while(1)
{
char buff[128] = {0};
int n = recv(c,buff,127,0);//阻塞运行
if(n <= 0)
{
printf("one client unlink!\n");
close(c);
break;
}
printf("%d: %s\n",c,buff);
send(c,"ok",2,0);
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd != -1);
struct sockaddr_in ser;//服务器的IP地址 端口号
memset(&ser,0,sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);//服务器上对应服务进程的端口号
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert(res != -1);
while(1)
{
printf("please input:");
char buff[128] = {0};
fgets(buff,128,stdin);
if(strncmp(buff,"end",3) == 0)
{
close(sockfd);
break;
}
send(sockfd,buff,strlen(buff)-1,0);
char recvbuff[128] = {0};
int n = recv(sockfd,recvbuff,127,0);
if(n <= 0)
{
close(sockfd);
break;
}
printf("client recv data: %s\n",recvbuff);
}
}
结果如下:
五、两者的对比
最后给大家简单总结下两者的区别
1.TCP:
(1)面向连接的协议,形成传输数据的通道;
(2)在连接中进行大量数据传输;
(3)通过三次握手完成连接,是可靠协议,但效率稍低。
2.UDP:
(1)将数据封装在数据包中,不需要建立连接;
(2)每个数据包的大小限制在64K以内;
(3)因无连接,是不可靠协议,但速度快。
3. 在注重速度的时候使用UDP---》如:视频聊天,QQ语音 QQ视频 时
在注重安全的时候使用TCP---》如:下载文件, 浏览器,TFTP ,HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议时