TCP和UDP的区别

UDP

UDP 协议全称是用户数据报协议,在网络中和TCP协议一样用于处理数据,是一种无连接的协议。在传输过程中,UDP不提供数据包分组、组装和排序,是无法保证数据包是否安全完整到达的。
它有以下几个特点:

  1. 无连接
    首先UDP是不需要建立连接的,具体来说就是:
  • 在发送端,应用层将数据传递给传输层的UDP协议,UDP只会给数据增加一个UDP头标识,然后就传递给网络层。
  • 在接受端,网络层将数据传递给传输层,UDP协议只会去除IP文的报头,然后就传递给应用层。
  1. 有单播、多播、广播的功能
    UDP不止支持一对一的传输方式,同样也支持一对多,多对多,多对一的方式

  2. UDP是面向报文的
    发送方的UDP对应用层的报文,在添加剂首部后就交给UP层,UDP不会对报文进行合并,拆分

  3. 不可靠性
    首先不可靠体现在无连接上,并且收到什么数据就传递什么数据,且不会备份数据,所以发送方也并不关心对方是否已经正确接收到数据了。再者UDP因为没有拥塞控制,不管网络环境的好坏,UDP都会以一个恒定的速度发送数据,这样的条件下有可能导致丢包的问题。在某些实时性要求高的场景(比如电话会议)就需要使用UDP而不是TCP

  4. 头部开销小,传输速度高效
    UDP头部包括以下几组数据:

  • 两个十六位的端口,分别是源端口和目标端口
  • 整个数据报文的长度
  • 整个数据报文的检验,该字段用于发现头部信息和数据中的错误
    因此UDP头部开销小,只有8个字节,相比TCP的至少12个字节要少的多,在传输数据报文是很搞笑的

TCP

TCP 协议全称传输控制协议,是一种面向连接、可靠的、基于字节流的传输层协议。
它有以下几个特点:

  1. 面向连接
    面向连接是指发送给数据前必须两端建立连接。建立的方法是'三次握手',这样才能建立一个可靠的连接,为后续可靠数据的传输奠定基础

  2. 仅支持单播传输
    每条TCP传输连接只能有2个端口,只能点对点进行传输

  3. 面向字节流
    TCP不像UDP一样一个个报文独立传输,而是在不保留报文边界的情况下以字节流方式进行传输

  4. 可靠传输
    对于可靠传输的方式是判断丢包,误码靠的是TCP的段编号以及确认号。TCP为了保证报文传输的可靠,就给每一个包一个序号,同时序号也保证传送到接收端实体的包的按序接收,然后接收端实体对已成功收到的字节返回一个相应的确认;如果发送端实体在合理的往返时延内未收到确认,那么对应数据将会重传

  5. 提供拥塞控制
    当网络出现拥塞的时候,TCP能够减少向网络注入数据的速率和数量,缓解拥塞

  6. TCP提供全双工通信
    TCP允许通信双方的应用程序在任何时候都能发送数据

TCP和UDP的对比

UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制
连接对象个数 支持一对一,一对多,多对一和多对多的交互通信 只能一对一通信
传输方式 面向报文 面向字节流
传输速度 快,效率高 慢,效率低
首部开销 首部开销小,仅8个字节 首部最小20字节,最大60字节
适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,如文件传输

衍生问题

  1. 丢包和粘包发生的原因
  • 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生丢包
  • 待发送的数据大于MSS(最大报文长度),TCP在传输前将进行丢包
  • 要发送的数据局小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包
  • 接收数据端的应用层没有及时读取接收区缓冲区中的数据,将发生粘包
  1. 丢包和粘包的解决方案
  • 发送端给每一个数据包添加包首部,在首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了
  • 发送端将每一个数据包封装为固定长度,这样接收端每次接收缓冲区读取固定长度的数据就自然而然的把每一个数据拆分开来。
  • 可以在数据包之间设置边界,如添加特殊符号,这样接收端通过这个边界就可以将不同得到数据包分开。
  1. TCP和UDP编程
  • TCP编程的服务端一般步骤
    1. 创建一个socket对象:socket_server = socket.socket()
    2. 设置socket对象属性:setsockopt(); * 可选
    3. 绑定IP地址、端口等信息:socket_server.bind()
    4. 监听socket对象:socket_server.listen()
    5. 接收客户端上来的连接:socket_server.accept()
    6. 收发数据:send()和recv(), 或者read()和write()
    7. 关闭网络连接
    8. 关闭监听

代码如下

 import socket

 # 创建TCP套接字
 tcp_socket= socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 # 绑定IP和端口
 addr = ("127.0.0.1", 9999)
 tcp_socket.bind(addr)

 # 监听,告诉系统创建链接队列,套接字改被动
 # 链接套接字,监听套接字
 tcp_socket.listen(128)

 # 接收客户端上来的连接
 new_sokcet, cli_addr = tcp_socket.accept()
 print(cli_addr, "连接成功")

 # 接收客户端的内容, 注意:使用的是新的套接字
 recv_data = new_sokcet.recv(1024)
 print(cli_addr, " >>> ", recv_data.decode())

 # 回复数据
 new_sokcet.send("ok".encode())

 # 关闭套接字
 new_sokcet.close()
 tcp_socket.close()
  • TCP客户端的一般步骤
    1. 创建一个socket对象,使用函数socket():socket_client = socket.socket()
    2. 设置socket对象属性:setsockopt(); * 可选
    3. 绑定IP地址、端口等信息:bind()
    4. 设置要连接对方的IP和端口等属性
    5. 连接服务器:connect()
    6. 收发数据:send()和recv(), 或者read()和write()
    7. 关闭网络连接

代码如下

import  socket

# 创建TCP套接字
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定IP地址、端口等信息
addr = ("127.0.0.1", 8888)
tcp.connect(addr)

# 发送数据
buf = "are u ok?"
tcp.send(buf.encode())

recv_data = tcp.recv(1024)
print(addr, " >>> ", recv_data.decode())

# 关闭套接字
tcp.close()
  • UDP编程服务端的步骤:
    1. 创建一个socket对象,使用函数socket():socket_server = socket.socket()
    2. 设置socket对象属性:setsockopt(); * 可选
    3. 绑定IP地址、端口等信息:bind()
    4. 循环接收数据:recvfrom();
    5. 关闭网络连接

代码如下:

import  socket

def recv_msg(udp):
    # 接收对方发送的数据
    recv_data = udp.recvfrom(1024)
    data, addr2 = recv_data
    # 接收的数据
    print(addr2, ">>>>>", data.decode())


def main():
    # 创建套接字
    # socket.AF_INET: ipv4
    # socket.SOCK_DGRAM: udp协议
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    addr = ("127.0.0.1", 8080)
    udp.bind(addr)

    while True:
        recv_msg(udp)


    # 关闭套接字
    udp.close()


if __name__ == '__main__':
    main()
  • UDP客户端的步骤
    1. 创建一个socket对象,使用函数socket():socket_client = socket.socket()
    2. 设置socket对象属性:setsockopt(); * 可选
    3. 绑定IP地址、端口等信息:bind()
    4. 设置要连接对方的IP和端口等属性
    5. 发送数据: sendto()
    6. 关闭网络连接

代码如下:

import  socket

def send_msg(udp):
    addr = ("127.0.0.1", 8080)

    buf = input("请输入需要发送的内容:")
    udp.sendto(buf.encode(), addr)


def main():
    # 功能:创建套接字
    # socket.AF_INET: ipv4
    # socket.SOCK_DGRAM: udp协议
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    while True:
       send_msg(udp)



    # 关闭套接字
    udp.close()

if __name__ == '__main__':
    main()

总结

  1. TCP向上层提供面向连接的可靠服务,UDP向上层提供无连接不可靠的服务
  2. 对数据准确性要求高的,速度相对较慢的,可以选择TCP
  3. 对实时性要求高的,可以选择UDP
posted @ 2022-03-17 20:59  KB、渣科  阅读(105)  评论(0编辑  收藏  举报