第二章 网络编程
python核心编程-第二章网络编程
- 2.1简介
- 2.2 客户端/服务器架构
- 2.2.0 概述
- 什么是客户端/服务器架构?
- 服务器:一系列硬件或者软件,为一个或多个客户端(服务的用户)提供所需的“服务”,它存在的唯一目的就是等待客户端的请求,并响应它们(提供服务),然后等待更多的请求
- 客户端:客户端因特定的请求而联系服务器,并发送必要的数据,然后等待服务器的回应,最后完成请求或给出故障原因。客户端的请求可被当做事务。
- 什么是客户端/服务器架构?
- 2.2.1 硬件客户端/服务器架构
- 打印机(打印服务器)
- 文件服务器(Sun公司的网络文件系统NFS)
- 2.2.2 软件客户端/服务器架构
- 介绍:
- 软件服务器提供的主要服务包括程序执行、数据传输检索、聚合、更新,或其他类型的编程或数据操作。
- 举例:
- web服务器
- 数据库服务器
- 窗体服务器
- 在哪运行?
- 一台附带(外接)显示设备的计算机上
- 客户端是什么?
- 图形用户界面(GUI)应用程序
- 特点:
- 需要一个窗口化的环境运行
- 在一个基于文本的环境中,无法启动GUI程序
- 在哪运行?
- 介绍:
- 2.2.3 银行出纳员作为服务器
- 银行出纳员/ATM与每个客户的互动模式就是客户端/服务器架构的工作方式。
- 2.2.4 客户端/服务器网络编程
- 服务器如何工作的?
- ①初步设置:创建通信端点,它可以使服务器监听请求
- ②服务器以广播的形式将自己的url告知客户端
- ③监听服务器开始工作,无限循环
- 客户端是如何工作的?
- ①创建它的单一通信端点
- ②建立一个客户端到服务器的连接
- ③发出请求,该请求包括任何必要的数据交换
- ④服务器处理请求且客户端收到结果/某种确认信息后,通信终止。
- 服务器如何工作的?
- 2.2.0 概述
- 2.3套接字:通信端点
- 2.3.1 套接字
- 是什么?
- 是计算机网络的数据结构,它体现了上节中所述的“通信端点”的概念
- 起源?
- 加利福尼亚大学伯克利版本UNIX(BSD UNIX)的一部分——伯克利套接字/BSD套接字
- 最初用于进程间通信
- 分类?
- 基于文件
- UNIX套接字
- 地址家族(address family):UNIX
- AF_UNIX或AD_LOCAL
- 其他较旧的系统
- 协议家族(protocol family)
- PF
- 为什么基于文件?
- 两个进程运行在同一台计算机上,所以这些套接字都是基于文件的,这意味着文件系统支持它们底层的基础结构。
- 文件系统是运行在同一个主机上的多个进程之间的共享常量
- UNIX套接字
- 面向网络
- 地址家族:因特网
- AF_INET
- AF_INET6:IPv6寻址
- 在所有地址家族中,AF_INET是应用最广泛的。
- 特殊类型Linux套接字
- AF_NETLINK无连接家族(python2.5)
- 支持透明的进程间通信 (TIPC)协议
- AF_TIPC协议(python2.6)
- python支持的家族
- AF_UNIX
- AF_NETLINK
- AF_TIPC
- AF_INET
- 基于文件
- 是什么?
- 2.3.2 套接字地址:主机-端口对
- 介绍
- 套接字——电话插孔
- 主机名——区号
- 端口——电话号码
- 网络地址
- 一个网络地址由主机名和端口号对组成
- 端口号
- 有效范围:0~65535
- 小于1024的端口号预留给了系统
- 介绍
- 2.3.3 面向连接的套接字与无连接的套接字
- 面向连接的套接字
- 定义
- 在进行通信之前必须建立一个连接。
- 这种类型的通信也被称为虚拟电路或流套接字
- 特点
- 序列化:消息可以拆分成多个片段,且有序号
- 可靠性:每一条消息片段都确保能够到达目的地
- 不重复:可按序号进行重组,不会重复
- 没有记录边界
- 主要协议
- 传输控制协议TCP
- 为了创建TCP套接字,必须使用SOCK_STREAM作为套接字类型
- 传输控制协议TCP
- 定义
- 无连接的套接字
- 定义
- 在通信开始前并不需要建立连接
- 也叫数据报
- 特点
- 无法保证顺序性、可靠性或重复性——消息以整体发送,不会分段
- 保存了边界记录
- 主要协议
- 用户数据报协议UDP
- 为了创建UDP套接字,必须使用SOCK_DGRAM作为套接字类型。
- 用户数据报协议UDP
- 使用无连接的原因
- 在面向连接的套接字中,相关设置和对虚拟电路的维护需要大量的开销,数据报不需要,这种情况下,数据报成本相对低廉。
- 定义
- 面向连接的套接字
- 2.3.1 套接字
- 2.4 python中的网络编程(可参考:https://www.runoob.com/python/python-socket.html)
- 2.4.0 概述
- socket模块
- socket()函数:创建套接字对象
- 套接字方法集:实现基于套接字的网络通信
- 2.4.1 socket()模块函数
- 创建套接字
- 必须使用socket.socket()函数
- 语法:
- socket(socket_family,socket_type,protocol=0)
- socket_family:AF_INET或AF_UNIX
- socket_type:SOCK_STREAM或SOCK_DGRAM
- protocol:通常省略,默认为0
- 创建TCP/IP套接字
- tcpSock=socket.socket(socket.AF_INET,SOCK_STREAM)
- 创建UDP/IP套接字
- udpSock=socket.socket(socket.AF_INET,SOCK_DGRAM)
- 创建套接字
- 2.4.2 套接字对象(内置)方法
- python核心编程P49页
- 2.4.3 创建TCP服务器
- (1)通用TCP服务器的一般伪代码
- ss = socket() # 创建服务器套接字
- ss.bind() # 套接字与地址绑定
- ss.listen() # 监听连接
- inf_loop: # 服务器无限循环
- cs = ss.accept() # 接受客户端连接
- comm_loop: # 通信循环
- cs.recv()/cs.send # 对话接收/发送
- cs.close()# 关闭客户端套接字
- ss.close() # 关闭服务器套接字
- (2)编程大概思路
- ①创建服务器套接字socket方法
- ②将服务器的套接字与某个本地地址绑定(相应端口号地址),即占用特定端口号bind方法
- TCP面向连接——需要建立连接——需要用到地址——需要绑定
- ③对可接入的连接进行监听listen方法
- ④使服务器进入无限循环运行状态while循环
- ⑤客户端请求连接时,接受客户端的连接accept方法
- 此时相当于开启了一个简单的(单线程)服务器
- 服务器接受连接后,会返回一个独立的客户端套接字,建立通信——临时客户端套接字创建
- ⑥ 开始通信,进入通信循环while
- ⑦双方互相发送/接收数据(发送:send方法,接收:recv方法)
- ⑧关闭连接close方法
- 当一方关闭连接或者像对方发送一个空字符串时,通常会掉用close方法
- (3)TCP时间戳服务器代码的例子
- python核心编程P52页示例2.1、2.2
-
1 from socket import * 2 from time import ctime 3 4 HOST = '' 5 PORT = 21567 6 BUFSIZ = 1024 7 ADDR = (HOST,PORT) 8 9 tcpSerSock = socket(AF_INET, SOCK_STREAM) 10 tcpSerSock.bind(ADDR) 11 tcpSerSock.listen(5) 12 13 while True: 14 print("waiting for connection...") 15 tcpCliSock, addr = tcpSerSock.accept() 16 print("...connected from:", addr) 17 18 while True: 19 data = tcpCliSock.recv(BUFSIZ) 20 if not data: 21 break 22 data1 = ctime() 23 data2 = str(data1) + ' ' + str(data) 24 data3 = data2.encode() 25 tcpCliSock.send(data3) 26 27 tcpCliSock.close() 28 tcpSerSock.close()
- (1)通用TCP服务器的一般伪代码
- 2.4.4 创建TCP客户端
- (1)通用TCP客户端的一般伪代码
- cs=socket()# 创建客户端套接字
- cs.connect()# 尝试连接服务器
- comm_loop: # 通信循环
- cs.send()/cs.recv()# 对话(发送/接收)
- cs.close()# 关闭客户端套接字
- (2)编程一般思路
- ①创建客户端套接字socket()方法
- ②尝试连接服务器connect()方法
- ③进入通信循环while
- ④发送/接收数据send()、recv()
- ⑤关闭客户端套接字close()
- (3)TCP时间戳客户端代码的例子
- python核心编程P54页示例2.3/2.4
-
1 from socket import * 2 3 HOST = 'localhost' 4 PORT= 21567 5 BUFSIZ = 1024 6 ADDR = (HOST, PORT) 7 8 tcpCliSock = socket(AF_INET, SOCK_STREAM) 9 tcpCliSock.connect(ADDR) 10 11 while True: 12 data = input('> ') 13 if not data: 14 break 15 data2 = data.encode() 16 tcpCliSock.send(data2) 17 data2 = tcpCliSock.recv(BUFSIZ) 18 if not data2: 19 break 20 print(data2) 21 22 tcpCliSock.close()
- (4)相关分享
- 【关于TCP和UDP的服务器和客户端的执行步骤】https://mbd.baidu.com/ma/s/tHl7eP6n
- (1)通用TCP客户端的一般伪代码
- 2.4.0 概述
-
- 2.4.5执行TCP服务器和客户端
- (1)应该先启动服务器(在任何客户端试图连接之前)
- 服务器:被动伙伴——等待连接——首先创建
- 客户端:主动伙伴——发起连接——在服务器之后创建
- (2)执行方式
- 命令行执行python文件
- (3)优雅的退出和调用服务器close()方法
- 在开发中:
- 将服务器的while循环放在一个try-except语句的except子句中,并监控EOFError或KeyboardInterrupt异常,这样就可以在except或finally子句中关闭服务器的套接字
- 在生产环境中
- 需要使用一个线程或创建一个特殊文件或数据库条目来设置一个标记以关闭服务。
- 在开发中:
- (4)实际执行过程中遇到的问题
- 编码格式很难受,服务器和客户端只接受发送bytes类型的数据,最终虽然成功通信了,但打印出来的结果和书上面的还是不一样。
- (1)应该先启动服务器(在任何客户端试图连接之前)
- 2.4.6 创建UDP服务器
- (1)通用伪代码
- ss = socket() # 创建服务器套接字
- ss.bind() # 套接字与地址绑定
- inf_loop: # 服务器无限循环
- cs=ss.recvfrom()/ss.sendto() # 关闭(接收/发送)
- ss.close() # 关闭服务器套接字
- (2)UDP时间戳服务器的例子
-
1 from socket import * 2 from time import ctime 3 4 HOST = '' 5 PORT = 21567 6 BUFSIZ = 1024 7 ADDR = (HOST, PORT) 8 9 udpSerSock = socket(AF_INET, SOCK_DGRAM) 10 udpSerSock.bind(ADDR) 11 12 while True: 13 print('waiting for message...') 14 data, addr = udpSerSock.recvfrom(BUFSIZ) 15 udpSerSock.sendto(data, addr) 16 print('...received from and returned to:', addr) 17 18 udpSerSock.close()
-
- (1)通用伪代码
- 2.4.7 创建UDP客户端
- (1)通用伪代码
- cs=socket() # 创建客户端套接字
- comm_loop: # 通信循环
- cs.sendto()/cs.recvfrom() # 对话(发送/接收)
- cs.close() # 关闭客户端套接字
- (2)UDP时间戳服务器例子
-
1 from socket import * 2 3 HOST = 'localhost' 4 PORT = 21567 5 BUFSIZ = 1024 6 ADDR = (HOST, PORT) 7 8 udpCliSock = socket(AF_INET, SOCK_DGRAM) 9 10 while True: 11 data = input('> ') 12 if not data: 13 break 14 data2 = data.encode() 15 udpCliSock.sendto(data2, ADDR) 16 data, ADDR = udpCliSock.recvfrom(BUFSIZ) 17 if not data: 18 break 19 print(data) 20 21 udpCliSock.close()
-
- (1)通用伪代码
- 2.4.8 执行UDP服务器和客户端
- 在命令行工具执行相应的.py文件即可
- 2.4.9 socket模块属性
- python核心编程P61页表2-2
- 2.4.5执行TCP服务器和客户端
- 2.5 SocketServer模块
- 2.5.0 简单概述
- (1)SocketServer是什么?
- 它是标准库中的一个高级模块,目的是简化创建网络客户端和服务器所必须的代码。
- (2)SocketServer里面有什么?
- 各种不同的类,在创建相应的客户端或者服务器时可以直接继承
- 具体见python核心编程P63页表2-3
- (3)以面向对象方式编写应用程序地特点
- 隐藏实现细节
- 有利于组织数据
- 应用程序变成事件驱动
- 只有在系统中的事件发生时,应用程序才会工作
- 事件包括消息的发送和接收
- (1)SocketServer是什么?
- 2.5.1 创建SocketServer TCP服务器
-
1 from socketserver import(TCPServer as TCP, StreamRequestHandler as SRH) 2 from time import ctime 3 4 HOST = '' 5 PORT = 21567 6 # BUFSIZ = 1024 7 ADDR = (HOST, PORT) 8 9 class MyRequestHandler(SRH): 10 def handle(self): 11 print('...connected from:', self.client_address) 12 data = self.rfile.readline() 13 data2 = data.encode() 14 self.wfile.write(bytes(ctime()), data2) 15 16 tcpServ = TCP(ADDR, MyRequestHandler) 17 print('waiting for connection...') 18 tcpServ.serve_forever()
-
- 2.5.2 创建SocketServer TCP客户端
-
1 from socket import * 2 3 HOST = 'localhost' 4 PORT = 21567 5 BUFSIZ = 1024 6 ADDR = (HOST, PORT) 7 8 while True: 9 tcpCliSock = socket(AF_INET, SOCK_STREAM) 10 tcpCliSock.connect(ADDR) 11 data = input('> ') 12 if not data: 13 break 14 data1 = data.encode() 15 tcpCliSock.send(data1) 16 data = tcpCliSock.recv(BUFSIZ) 17 if not data: 18 break 19 print(data.strip()) 20 tcpCliSock.close()
-
- 2.5.3 执行TCP服务器和客户端
- 2.5.0 简单概述
- 2.6 Twisted框架介绍
- 2.6.0 概述
- (1)Twisted是什么?
- 一个完整的事件驱动的网络框架
- (2)功能
- 利用它既能使用也能开发完整的异步网络应用程序和协议
- (3)作用是什么?
- 它提供了大量的支持来建立完整的系统,包括网络协议、线程、安全性和身份验证、聊天/IM、DBM及RDBMS数据库集成、Web/因特网、电子邮件、命令行参数、GUI集成工具包等
- (4)我们主要用什么?
- Twisted因特网组件中的reactor和protocol子包中的类
- (1)Twisted是什么?
- 2.6.1创建Twisted Reactor TCP服务器
- P66例2-10
- 2.6.2创建Twisted Reactor TCP客户端
- P68例2-11
- 2.6.3 执行TCP服务器和客户端
- 2.6.0 概述
- 2.7 相关模块
- P70页表2-4