python网络编程(三)
传输层编程
传输层
- 网络层的任务是提供主机到主机的通信,而传输层的任务是提供进程到进程的通信。
- UDP(User Datagram Protocol)用户数据报协议
- 采用无连接方式发送数据。
- UDP面向无连接,不可靠,但因为不用传送许多于数据本身无关的信息,所以效率高。
- TCP(Transmission Control Protocol)传输控制协议
- 是一个可靠的、面向连接的协议。
- 面向连接的TCP协议效率较低,但可靠性高,适合于网络链路不好或可靠性要求高的环境。
- 进程与端口号
- 在传输层中,使用端口(0~65535)来定义进程,端口号只具有本地意义。
- 模型
- 采用客户端-服务器端模式进行进程到进程的通信。
- 运行在本地主机上的程序称为客户端,运行在远程主机上提供服务的程序称为服务器端。
- 客户端的端口号通常随机选取,而服务器端的端口号一般是由IANA(互联网数字分配机构)指派的知名端口号。
- 服务器端在知名端口上等待客户端的请求,客户端向服务器端上的知名端口发送服务请求,并得到响应。
- 端口号的分配
- 知名端口:0~1023,由IANA指派和控制。
- 21/TCP FTP 文件传输协议
- 22/TCP SSH 安全登录
- 23/TCP Telnet 远程登录
- 25/TCP SMTP 简单邮件传输协议
- 80/TCP HTTP 超文本传输协议
- 53/UDP DNS 域名解析
- 69/UDP TFTP 简单文件传输协议
- 161/UDP SNMP 简单网络管理协议
- 注册端口:1024~49151,IANA不指派也不控制,但需要在IANA注册以防止重复。
- 动态端口:49152~65535,不需要向IANA注册,可以由任何进程使用,也称短暂端口。
- 知名端口:0~1023,由IANA指派和控制。
UDP
-
UDP数据包
-
Python模拟UDP
-
UDP服务端
udp_serber.py
import socket """ 创建socket并指定socket的类型为UDP类型 服务端在启动之后,会等待客户端的连接 """ with socket.socket(type=socket.SOCK_DGRAM) as server: # 绑定IP地址并设置端口号,127.0.0.1是网络回环地址,永远在线,用于测试当前主机是否在线 server.bind(('127.0.0.1', 1000)) print("成功访问UDP服务端!") # recvfrom该方法返回了客户端的地址与端口号,以及服务器端节接收到的数据 # 1024代表服务器端接收的数据的长度 data, address = server.recvfrom(1024) print(f"address : {address[0]}, port : {address[1]}, data : {data.decode()}") # 向客户端发送数据,address是客户端向服务器端发送数据时,所捕获的地址信息,包含IP地址 server.sendto("访问成功!".encode(), address)
-
UDP客户端
udp_client.py
import socket with socket.socket(type=socket.SOCK_DGRAM) as client: send_data = "我是客户端" client.sendto(send_data.encode(), ('127.0.0.1', 1000)) client_data, address = client.recvfrom(1024) print(f"收到的数据为:{client_data.decode()}")
-
运行代码
-
运行
udp_serber.py
-
打开CMD运行
netstat -an
可以看出1000端口已经启动。 -
运行
udp_client.py
-
查看
udp_server.py
,客户端的端口是随机端口
-
-
-
Python实现聊天室功能
server.py
import socket """ 创建socket并指定socket的类型为UDP类型 服务端在启动之后,会等待客户端的连接 """ with socket.socket(type=socket.SOCK_DGRAM) as server: # 绑定IP地址并设置端口号,127.0.0.1是网络回环地址,永远在线,用于测试当前主机是否在线 server.bind(('127.0.0.1', 1000)) # 设置自己的名字 nickname = input("请输入你的名字:") print("服务端以连接,等待客户端连接") while True: # recvfrom该方法返回了客户端的地址与端口号,以及服务器端节接收到的数据 # 1024代表服务器端接收的数据的长度 data, address = server.recvfrom(1024) # 接收客户端的信息并打印 print(data.decode()) send_msginput("请输入要发送的信息:") if send_msg == 'quit': # 输入quit表示已下线 server.sendto(f"{nickname}已下线".encode(), address) break # 向客户端发送数据,address是客户端向服务器端发送数据时,所捕获的地址信息,包含IP地址 server.sendto(f"{nickname}: {send_msg}".encode(), address)
client.py
import socket nickname = input("请输入你的名字:") with socket.socket(type=socket.SOCK_DGRAM) as client: while True: send_msg = input("请输入要发送的内容:") if send_msg == 'quit': client.sendto(f"{nickname}已下线".encode(), ('127.0.0.1', 1000)) break client.sendto(f"{nickname}: {send_msg}".encode(), ('127.0.0.1', 1000)) client_data, address = client.recvfrom(1024) print(client_data.decode())
运行
server.py
,client.py
TCP
-
格式
- Source Port:16位的源端口号;包含初始化的通信端口,其中的源端口、源IP地址标示报文的返回地址。
- Destination Port:16位的目的端口号;定义了传输的目的地,指明了应用程序的地址接口。
- Sequence Number:32位的序列号;用于接收端,标示数据包,例如数据包的排序、拼接顺序。
- Acknowledge Number:32位的确认序号;用于接收端,确认该数据包。
- Offset:4位的数据偏移;TCP首部(包括选项)长度除以4。
- Reserved:6位的保留位;必须为0,为了将来用于新的用途。
- Flags:标志位;6种标志,详情见百度百科。
- Windows:表示接收缓冲区的空闲空间,16位,用来告诉TCP连接对端自己能够接收的最大数据长度,最大为65535。
- Checksum:16位的校验和,证明数据的有效性。
- Urgent Pointers:16位的紧急指针;只有URG标志位被设置时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。
- Options:选项。
- data:上层协议的数据。
-
三次握手
-
四次挥手
-
Python 模拟TCP
tcp_server.py
import socket """ 创建服务器端的socket SOCK_STREAM代表TCP协议 """ with socket.socket(type=socket.SOCK_STREAM) as server: # 绑定IP地址和端口号 server.bind(("127.0.0.1", 1000)) # 在bind中是传递的元祖 print("TCP服务器已经启动") # 设置最大连接数,必须大于1 server.listen(100) # 接收客户端的连接,当有客户端连接上来之后,会从服务端的socket里面创建出一个新的socket,新的socket用于服务于客户端 client, address = server.accept() # address返回的是一个元祖 print(f"当前的客户端的地址为:{address[0]},端口号为:{address[1]}") # 这里由于accept获取到了客户端的信息,因此接收数据使用了recv方法 data = client.recv(1024) print(f"客户端发送的数据为:{data.decode()}") client.send("服务端收到消息".encode()) # 关闭客户端的连接 client.close()
tcp_client.py
import socket # 创建客户端的socket with socket.socket(type=socket.SOCK_STREAM) as client: # 哭护短连接服务器 client.connect(("127.0.0.1", 1000)) # 给服务端发送数据 client.send("你好,我是客户端".encode()) # 从服务端接收数据 recv_data = client.recv(1024) # 打印服务端发送的数据 print(f"服务端发送的数据为:{recv_data.decode()}")
-
执行
tcp_server.py
,tcp_client.py