python提供了两个不同层次的网络编程API:
- 基于Socket的低层次网络编程,采用UDP, TCP等协议
- 基于Url的高层次网络编程,采用 HTTP 和 HTTPS
网络编程的基础知识
网络结构:网络结构是网络的构建方式,目前流行的是客户端服务器结构网络和对等结构网络。
客户端服务器结构网络(C/S)是一种主从结构网络。服务器一般处于等待状态,如果有客户端请求,服务器响应请求,建立连接提供服务。服务器是被动的状态。例子:Web服务,邮件服务,文件传输服务。存在目的不同,但是结构类似。网络结构与设备种类无关,手机也可以当服务器。
对等结构网络(P2P)也叫点对点(Peer to Peer)结构网络。每个节点之间是对等的。每个节点既是服务器也是客户端。范围较小,通常在一间办公室和家庭中。非常适合移动设备间的网络通信。
TCP/IP协议:网络通信需要用到协议。TCP/IP协议是非常重要的,分为TCP协议和IP协议。
IP协议是一种低级的路由协议,它将数据分为很多小的数据包,然后通过网络发送到特定地点,但是无法保证所有包都能到达指定目的地,也无法保证包的到达顺序。
由于IP协议的不安全性,网络通信还需要TCP(传输控制协议)协议,是一种面向连接的可靠传输高级协议。保证未收到的包会进行重发,同时对数据包内容的准确性进行检查并保证数据包的顺序。所以TCP协议能够保证所有的数据包能够安全的按照顺序发送到特定地点。
IP地址:为了实现不同计算机之间的通信,每个计算机都必须有一个与众不同的标志,这就是IP地址。TCP/IP使用IP地址来标志源地址和目的地址。
最初的IP地址有32位,由四个8位的二进制组成。例如192.168.1.1,这种类型的地址通过IPV4指定。
而现在有种新的地址模式称为 IPv6, lPv6 使用 128 数字表示一个地址,分为八个
16 位块。尽管IPV6比较IPV4有很多优势,但是目前还是IPV4使用的较多。python语言同时支持IPV4和IPV6
IPv4 地址模式中IP地址分为A,B,C,D,E类。
- A类地址用于大型网络,地址范围:1.0.0.1 — 126.155.255.254
- B类地址用于中型网络,地址范围:128.0.0.1 — 191.255.255.254
- C类地址用于小规模网络,地址范围:192.0.0.1 — 255.255.255.254
- D类地址多用于目的地信息的传输和备用
- E类地址保留,仅做实验和开发用
- 一个特殊的IP地址,127.0.0.1,称为回送地址,即本机。
端口:一个IP地址代表一台计算机,一台计算机会运行很多个网络通信程序。这就需要不同的端口进行通信
TCP/IP系统中的端口是一个16位数字,范围是0-65535。小于1024的端口号是留给预定义服务的.
TCP Socket:Socket是指网络上的两个程序,通过一个双向的通信连接,实现数据的交换。
这个双向连接的一段,称为一个Socket。一个Socket由一个地址和一个端口唯一确定,一旦连接Socket还会包含本机和远程主机的IP地址和端口号。Socket是成对出现的。
TCP Socket通信过程
python提供了两个Socket模块,Socket和SocketServer,前者提供了标准的BSD Socket API. Socket的重点是网络服务器开发,提供了四个基本服务器类,简化服务器开发。
创建TCP Socket:
import socket
# socket.AF_INET: IP地址类型是IPV4。 IPV6的参数是 AF_INET6.
# socket.SOCK_STREAM:设置通信类型是TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Socket对象有很多方法,与TCP Socket服务器有关的方法如下:
socket.bind(address):绑定地址和接口。address是包含主机名(或IP地址)和端口的二元组对象。
socket.listen(backlog):监听端口。backlog是最大连接数,默认为1.
socket.accept():等待客户端连接,连接成功返回一个二元组对象(conn, address), conn是新的scocket对象,用来发送或接收数据,address是客户端的地址。
客户端socket方法:
socket.connect(address):连接服务器。address是一个包含主机地址(IP对象)和端口的二元组。
服务器和客户端Socket编程共用方法:
socket.revc(buffsize):接收TCP Socket数据,返回一个字节序列对象。参数buffsize指定一次接收的最大字节数,如果要接收的数据量大于buffsize,则需要多次调用该方法进行接收
socket.send(bytes):发送TCP Socket数据,将bytes数据发送到远程Socket.返回发送成功的字节数。如果发送的数据量过大,需要多次调用这个方法。
socket.sendall(bytes):发送TCP Socket数据,发送成功返回None,失败返回异常。与socket.send(bytes)不同的是,该方法会一直发送bytes数据,直至数据发送完毕或发生异常。
socket.settimeout(timeout):设置超时时间。timeout是一个浮点数,单位是s。设置为None则永不超时。一般超时时间都是在刚创建socket时创建。
socket.close():该方法可以释放资源。但是不一定会立刻关闭连接。如果想要立刻关闭连接,需要在之前执行shutdown()方法。
python中socket对象是可以进行垃圾回收的,当socket对象被回收时会自动关闭,但还是建议显式的调用close()进行关闭。
案例:简单聊天工具
import socket
import threading
class TestSocketServerThread(threading.Thread):
def __init__(self):
super(TestSocketServerThread, self).__init__()
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socket
# IP地址为空,系统会自动分配本机可用的IP地址
s.bind(("", 8888)) # 绑定地址
s.listen() # 监听本机8888号端口
print("服务器启动...")
# 等待客户端连接
conn, address = s.accept()
# 客户端连接成功
print(address)
# 接收数据 返回的是字节序列对象。
# decode()可将字符序列转换为字符串对象,可指定编码,默认为utf-8
data = conn.recv(1024)
print("从客户端接收消息:{0}".format(data.decode()))
# 给客户端发送消息
conn.send("你好".encode())
# 释放资源
conn.close()
s.close()
class TestSocketClientThread(threading.Thread):
def __init__(self):
super(TestSocketClientThread, self).__init__()
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socket
# '127.0.0.1'是远程服务器IP地址或主机名
s.connect(('127.0.0.1', 8888)) # 绑定地址
# 给客户端发送消息
# 在字符串前加 b,可将字符串转换为字节序列,这种方法只适合ASCII字符串
s.send(b'hello')
# 从服务器端接收数据
data = s.recv(1024)
print("从服务端接收消息:{0}".format(data.decode()))
# 释放资源
s.close()
ServerThread = TestSocketServerThread()
ClientThread = TestSocketClientThread()
ServerThread.start()
ClientThread.start()
简单的文件上传工具案例
import socket
import threading
class TestScoketServerthread(threading.Thread):
def __init__(self):
super(TestScoketServerthread, self).__init__()
def run(self):
Host = ""
Post = 8888
f_name = "aaaa.txt"
# 使用with as自动管理Socket对象
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((Host, Post))
s.listen(10)
print("服务器已启动...")
while True:
with s.accept()[0] as conn:
# 创建字节序列对象列表,作为接收数据的缓冲区
buffer = []
while True:
data = conn.recv(1024)
if data:
buffer.append(data)
else:
break
b = bytes().join(buffer)
with open(f_name, 'wb') as f:
f.write(b)
print("服务器接收完成")
class TestScoketClientthread(threading.Thread):
def __init__(self):
super(TestScoketClientthread, self).__init__()
def run(self):
HOST = '127.0.0.1'
PORT = 8888
f_name = "bbbb.txt"
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
with open(f_name, 'rb') as f:
data = f.read()
s.sendall(data)
print("客户端数据上传成功")
server = TestScoketServerthread()
client = TestScoketClientthread()
server.start()
client.start()
UDP(用户数据报协议):
无连接,对系统资源要求较少。
不能可靠的寄到目的地,传达时的顺序不能保证。
但是对网络游戏,在线视频等传输快,实时性高,质量可稍差一点的传输。
Socket模块中的UDP API与TCP类似,都是使用socket对象,只是参数有所不同。
import socket
# socket.AF_INET: IP地址类型是IPV4。 IPV6的参数是 AF_INET6.
# socket.SOCK_DGRAM:设置通信类型是UDP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
UDP Server Socket方法
socket.bind():不需要accpet和listen。upd传输不需要建立连接
服务器与客户端socket共用方法
socket.recvfrom(buffsize):接收UDP Socket数据。
返回二元组(data, address),前者是接收的字符序列对象,后者是发送数据的Socket地址。
参数buffsize指定一次接收的最大数据量,如果接收数据量大于buffsieze,则需要多次调用该方法发送数据
socket.sendto(bytes, address):将字符序列bytes,发送到地址为address的远程socket。(address是一个socket对象)
若发送数据量较大,则需要多次运行此方法
socket,settimeout(time):设置超时时间,同TCP
socket.close():关闭socket,同TCP
案例:UPD的简单聊天工具
UDP传输工具与TCP有所不同,UDP接收数据时,并不能了解数据是否已经传输完成,因此在上传数据时,通常会在上传数据完成后上传一个关键字,服务器接收到关键字后,能够判断数据已经接收完毕。
import socket
import threading
class TestUDPServerThread(threading.Thread):
def __init__(self):
super(TestUDPServerThread, self).__init__()
def run(self):
# 建立socket, 参数2使用UDP传输
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 8888))
print("服务器启动")
data, client_address = s.recvfrom(1024)
print("从客户端接收的数据: {0}".format(data.decode()))
# 向客户端发送数据
s.sendto("你好".encode(), client_address)
s.close()
class TestUDPClientThread(threading.Thread):
def __init__(self):
super(TestUDPClientThread, self).__init__()
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('127.0.0.1', 8888)
s.sendto(b'hello', server_address)
data, _ = s.recvfrom(1024)
print("从服务器接收的数据: {0}".format(data.decode()))
s.close()
server = TestUDPServerThread()
client = TestUDPClientThread()
server.start()
client.start()
UDP文件上传工具示例
import socket
import threading
class TestUDPServerThread(threading.Thread):
def __init__(self):
super(TestUDPServerThread, self).__init__()
def run(self):
# 建立socket, 参数2使用UDP传输
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 8888))
print("服务器启动")
data, client_address = s.recvfrom(1024)
print("从客户端接收的数据: {0}".format(data.decode()))
# 向客户端发送数据
s.sendto("你好".encode(), client_address)
s.close()
class TestUDPClientThread(threading.Thread):
def __init__(self):
super(TestUDPClientThread, self).__init__()
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('127.0.0.1', 8888)
s.sendto(b'hello', server_address)
data, _ = s.recvfrom(1024)
print("从服务器接收的数据: {0}".format(data.decode()))
s.close()
server = TestUDPServerThread()
client = TestUDPClientThread()
server.start()
client.start()