UDP 和 TCP 的区别
TCP | UDP | |
---|---|---|
连接性 | 面向连接 | 面向无连接 |
传输可靠性 | 可靠 | 不可靠 |
传输模式 | 流 | 数据报 |
应用场景 | 传输大量的数据 | 少量数据 |
速度 | 慢 | 快 |
TCP:
TCP 的可靠体现在传输数据之前,会有三次握手来建立连接。在数据传完后,还会断开连接用来节约系统资源。在数据传递时,有确认机制、重传机制、拥塞控制机制以保证传输的可靠性,但这些机制都会消耗大量的时间和系统资源,每个连接都会占用系统的 CPU、内存等硬件资源,所以也导致 TCP 容易被人利用,比如 DDOS、CC 等攻击。
一般用于文件传输、收发邮件或远程登录等对数据准确性要求高的场景。
UDP:
UDP 没有 TCP 那些可靠的机制,所以在数据传递时,如果网络质量不好,就会很容易丢包。但 UDP 也是无法避免攻击的,比如:UDP Flood 攻击。
一般用于即时通讯、在线视频、网络电话等对传输效率要求高,但对准确性要求相对低的场景。
- 面向有连接型
发送数据之前,需要在收发主机之间建立一条通信线路,在通信传输前后,专门进行建立和断开连接的处理,如果与对端之间无法通信,可避免发送无谓的数据。
- 面向无连接型
这种类型不要求建立和断开连接,发送端可任何时候发送数据,接收端也不知道自己何时从哪里接受数据,这种情况下,接收端需要时常确认是否收到数据,彼此也不需要确认对方是否存在。
关于 TCP 网络编程的实现请参考我的另一篇博文 Socket 通信原理
UDP 网络编程实现
面向无连接的 UDP 时序图
创建 Socket
sock = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
-
family -> 地址族
- socket.AF_UNIX: 用于同一台机器上的进程通信(既本机通信)
- socket.AF_INET: 用于服务器与服务器之间的网络通信
- socket.AF_INET6: 基于 IPV6 方式的服务器与服务器之间的网络通信
-
type -> Socket 对象的类型
- socket.SOCK_STREAM: 基于 TCP 的流式 Socket 通信
- socket.SOCK_DGRAM: 基于 UDP 的数据报式 Socket 通信
- socket.SOCK_RAW: 原始套接字,普通的套接字无法处理 ICMP、IGMP 等网络报文,而 SOCK_RAW 可以;其次 SOCK_RAW 也可以处理特殊的 IPV4 报文;此外,利用原始套接字,可以通过 IP_HDRINCL 套接字选项由用户构造 IP 头
- socket.SOCK_SEQPACKET: 可靠的连续数据包服务
- proto -> 协议编号,默认是 0,一般可以忽略该参数
- fileno -> Socket 的文件描述符,如果指定了 fileno,则其他参数将被忽略,返回指定文件描述符的 Socket。
创建 TCP Socket:sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
创建 UDP Socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
UDP 通信
sock.recvfrom(bufsize[, flags])
接受 UDP 套接字的数据,与 recv() 类似,但返回值是 tuple(data, address)
。其中 data 是包含接受数据的字符串,address 是发送数据的 Socket 地址
注意协议接收到的数据可能大于 buf 的长度,所以在这种情况下要调用几次 recv 函数才能把 Socket 接收缓冲区中的数据 copy 完。recv 函数仅仅是 copy 数据,真正接收数据是由协议来完成的。
sock.sendto(bytes, address)
发送 UDP 数据,将数据发送到 Socket,address 形式为 tuple(ipaddr, port)
,指定远程地址发送,返回值是发送的字节数
Python 2.x 发送的报文是 str 类型,Python 3.x 发送的报文是 bytes 类型,在发送前要记得编码。
客户端代码
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ('127.0.0.1', 8020)
while True:
msg = input('Wanna send: ')
if not msg:
break
sock.sendto(bytes(msg, 'utf-8'), address) # Return the number of bytes sent
data, addr = sock.recvfrom(1024)
data = data.decode('utf-8')
print('Response:', data)
sock.close()
服务端代码
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('127.0.0.1', 8020))
print('waiting for message...')
while True:
data, addr = sock.recvfrom(1024)
data = data.decode('utf-8')
print('Got message from', addr)
print('Received message:', data)
sock.sendto(bytes('[%s] %s' % (time.ctime(), data), 'utf-8'), addr)
sock.close()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。