标签:格式 模式 tcp ocs The 脚本 资源 单点 hello
在上一篇文章「Python 绝技 —— TCP 服务器与客户端」中,介绍了传输层的核心协议 TCP ,并运用 Python 脚本的 socket 模块演示了 TCP 服务器与客户端的通信过程。
本篇将按照同样的套路,先介绍传输层的另一个核心协议 UDP,再比较 TCP 与 UDP 的特点,最后借助 Python 脚本演示 UDP 服务器与客户端的通信过程。
UDP(User Datagram Protocol,用户数据报协议)是一种无连接、不可靠、基于数据报的传输层通信协议。
为了更直观地比较 TCP 与 UDP 的异同,笔者将其整理成以下表格:
TCP | UDP | |
---|---|---|
连接模式 | 面向连接(单点通信) | 无连接(多点通信) |
传输可靠性 | 可靠 | 不可靠 |
通信模式 | 基于字节流 | 基于数据报 |
报头结构 | 复杂(至少20字节) | 简单(8字节) |
传输速度 | 慢 | 快 |
资源需求 | 多 | 少 |
到达顺序 | 保证 | 不保证 |
流量控制 | 有 | 无 |
拥塞控制 | 有 | 无 |
应用场合 | 大量数据传输 | 少量数据传输 |
支持的应用层协议 | Telnet、FTP、SMTP、HTTP | DNS、DHCP、TFTP、SNMP |
Network Socket(网络套接字)是计算机网络中进程间通信的数据流端点,广义上也代表操作系统提供的一种进程间通信机制。
进程间通信(Inter-Process Communication,IPC)的根本前提是能够唯一标示每个进程。在本地主机的进程间通信中,可以用 PID(进程 ID)唯一标示每个进程,但 PID 只在本地唯一,在网络中不同主机的 PID 则可能发生冲突,因此采用「IP 地址 + 传输层协议 + 端口号」的方式唯一标示网络中的一个进程。
小贴士:网络层的 IP 地址可以唯一标示主机,传输层的 TCP/UDP 协议和端口号可以唯一标示该主机的一个进程。注意,同一主机中 TCP 协议与 UDP 协议的可以使用相同的端口号。
所有支持网络通信的编程语言都各自提供了一套 socket API,下面以 Python 3 为例,讲解服务器与客户端建立 UDP 通信连接的交互过程:
可见,UDP 的通信过程比 TCP 简单许多,服务器少了监听与接受连接的过程,而客户端也少了请求连接的过程。客户端只需要知道服务器的地址,直接向其发送数据即可,而服务器也敞开大门,接收任何发往自家地址的数据。
小贴士:由于 UDP 采用无连接模式,可知 UDP 服务器在接收到客户端发来的数据之前,是不知道客户端的地址的,因此必须是客户端先发送数据,服务器后响应数据。而 TCP 则不同,TCP 服务器接受了客户端的连接后,既可以先向客户端发送数据,也可以等待客户端发送数据后再响应。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 6000))
print("UDP bound on port 6000...")
while True:
data, addr = s.recvfrom(1024)
print("Receive from %s:%s" % addr)
if data == b"exit":
s.sendto(b"Good bye!\n", addr)
continue
s.sendto(b"Hello %s!\n" % data, addr)
b"exit"
,则向地址为 addr 的客户端发送结束响应信息 b"Good bye!\n"
。发送完毕后,继续等待其他 UDP 客户端发来数据。b"exit"
,则向地址为 addr 的客户端发送问候响应信息 b"Hello %s!\n"
,其中 %s
是客户端发来的 bytes 对象。发送完毕后,继续等待任意 UDP 客户端发来数据。与 TCP 服务器相比,UDP 服务器不必使用多线程,因为它无需为每个通信过程创建独立连接,而是采用「即收即发」的模式,又一次体现了 UDP 的无连接特性。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ("127.0.0.1", 6000)
while True:
data = input("Please input your name: ")
if not data:
continue
s.sendto(data.encode(), addr)
response, addr = s.recvfrom(1024)
print(response.decode())
if data == "exit":
print("Session is over from the server %s:%s\n" % addr)
break
s.close()
"exit"
时,则打印会话结束信息,终止与服务器交互数据的循环阶段,即将关闭套接字。将 UDP 服务器与客户端的脚本分别命名为 udp_server.py
与 udp_client.py
,然后存至桌面,笔者将在 Windows 10 系统下用 PowerShell 进行演示。
小贴士:读者进行复现时,要确保本机已安装 Python 3,注意笔者已将默认的启动路径名
python
改为了python3
。
python3 ./udp_server.py
,服务器绑定本地主机的 UDP 6000 端口,并打印信息 UDP bound on port 6000...
,等待客户端发来数据;python3 ./udp_client.py
,并向服务器发送字符串 Client1
、Client2
;Client1
发送空字符串,则被要求重新输入;Client2
先发送字符串 Alice
,得到服务器的问候响应信息,再发送字符串 exit
,得到服务器的结束响应信息,最后打印会话结束信息,终止与服务器的数据交互;Client1
发送字符串 exit
,得到服务器的结束响应信息,并打印会话结束信息,终止与服务器的数据交互;本节介绍上述代码中用到的内建模块 socket,是 Python 网络编程的核心模块。
socket() 函数用于创建网络通信中的套接字对象。函数原型如下:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
创建完套接字对象后,需使用对象的内置函数完成网络通信过程。注意,以下函数原型中的「socket」是指 socket 对象,而不是上述的 socket 模块。
bind() 函数用于向套接字对象绑定 IP 地址与端口号。注意,套接字对象必须未被绑定,并且端口号未被占用,否则会报错。函数原型如下:
socket.bind(address)
sendto() 函数用于向远程套接字对象发送数据。注意,该函数用于 UDP 进程间的无连接通信,远程套接字的地址在参数中指定,因此使用前不需要先与远程套接字连接。相对地,TCP 进程间面向连接的通信过程需要用 send() 函数。函数原型如下:
socket.sendto(bytes[, flags], address)
"hello world!"
而言,需要用 encode() 函数转换为 bytes 对象 b"hello world!"
才能进行网络传输。|
隔开。详情可参考 Unix 函数手册中的 sendto(2),flags 参数的常见取值有 MSG_OOB、MSG_EOR、MSG_DONTROUTE 等。sendto() 函数的返回值是发送数据的字节数。
recvfrom() 函数用于从远程套接字对象接收数据。注意,与 sendto() 函数不同,recvfrom() 函数既可用于 UDP 进程间通信,也能用于 TCP 进程间通信。函数原型如下:
socket.recvfrom(bufsize[, flags])
|
隔开。详情可参考 Unix 函数手册中的 recvfrom(2),flags 参数的常见取值有 MSG_OOB、MSG_PEEK、MSG_WAITALL 等。recvfrom() 函数的返回值是二元组 (bytes, address),其中 bytes 是接收到的 bytes 对象数据,address 是发送方的 IP 地址与端口号,用二元组 (host, port) 表示。注意,recv() 函数的返回值只有 bytes 对象数据。
close() 函数用于关闭本地套接字对象,释放与该套接字连接的所有资源。
socket.close()
本文介绍了 UDP 协议的基础知识,并与 TCP 协议进行对比,再用 Python 3 实现并演示了 UDP 服务器与客户端的通信过程,最后将脚本中涉及到的 Python API 做成了的参考索引,有助于读者理解实现过程。
感谢各位的阅读,笔者水平有限,若有不足或错误之处请谅解并告知,希望自己对 TCP 和 UDP 的浅薄理解,能帮助读者更好地理解传输层协议。
本文的相关参考请移步至:
标签:格式 模式 tcp ocs The 脚本 资源 单点 hello
原文地址:https://www.cnblogs.com/ichunqiu/p/9200723.html