Python网络编程基础
- IP地址
- ip地址表现形式
- 查看ip地址
- Linux
- Windows
- 检查网络是否正常
- 端口与端口号
- 端口号分类
- 知名端口号
- 动态端口号
- TCP协议
- 概念
- TCP通讯步骤
- 特点
- socket套接字
- TCP客户端程序开发
- 步骤
- 1:创建客户端程序的套接字对象
- socket模块
- 导入
- socket类
- socket对象创建
- 2:和服务端程序建立连接
- connect方法
- 3:发送数据
- 字符串编码
- encode()
- send方法
- 4:接收数据
- recv方法
- 字符串解码
- decode()
- 5:关闭客户端程序的套接字
- close方法
- 客户端程序代码汇总:
- TCP服务端程序开发
- 步骤
- 1:创建服务端程序的套接字对象
- 2:绑定端口号
- bind方法
- 设置端口号复用
- 3:设置监听
- listen()方法
- 4:等待接受客户端程序的连接请求
- accept()方法
- 5:接收数据
- 6:发送数据
- 7:关闭服务端程序的套接字
- 服务端程序代码汇总:
- 多任务版服务端程序
IP地址
IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
换而言之,IP 地址就是标识网络中设备的一个地址,好比现实生活中某家公司的办公地址。通过办公地址,能找到某家公司。类似的,通过IP地址,能够找到想找的某台能够进行网络通信的设备。
ip地址表现形式
IP 地址分为两类: IPv4 和 IPv6
IPv4 是目前正在使用的ip地址。它由4段.
分隔的十进制数字组成,每一段最大不超过255。(点分十进制)
互联网蓬勃发展,ip地址需求量迅速增大,为了避免ip地址用完,人们又设计出新的ip地址定义。IPv6出现。
IPv6由8段用:
分隔的十六进制数字组成,每一段不超过四位。(冒号十六进制)
IPv6一劳永逸地解决了ip地址不够的问题,还改善了IPv4的不少问题。
不过,现在还不急于推广IPv6。IPv4尚且够用。所以说,IPv6是未来使用的ip地址。
查看ip地址
查看ip地址的方式有很多,这里介绍终端命令行方式。
Linux
ifconfig
- 全称:network interfaces configuring
- ens33,lo表示网卡名
- ens33表示以太网,其inet表示设备在网络中的ip地址
- lo全称“loopback”,表示“回环”网络接口。它不代表真正的网络接口,而是一个虚拟的网络接口, 其 IP 地址默认是127.0.0.1,通常仅用于对本机的网络测试。
- 域名(通常意义上的网址)是ip地址的别名,通过域名可以解析出对应的ip地址。如域名
localhost
表示回环地址127.0.0.1。
Windows
ipconfig
检查网络是否正常
ping 域名/ip地址
- ping www.baidu.com 检查是否能上公网(外部网络)
- ping 当前局域网的ip地址 检查是否在同一个局域网内
- ping 127.0.0.1 检查本地网卡是否正常
- 正常的话会显示接收数据
- 不正常的话什么都没有,光标一直在等待
端口与端口号
端口(port),又称为连接端口、协议端口(protocol port)。端口是数据传输的通道。
通过ip地址找到某台网络设备后,要想与某个特定的程序(进程)进行数据传输,就要找到该程序(进程)对应的端口,通过端口,进行数据传输。
端口号是操作系统为了统一管理各程序的端口而编的号。端口号共有65536个。
ip地址相当于公司的办公地址,通过办公地址找到公司后,要想和公司的某个部门通信,就要找到部门的”门“,这个“门”就是端口,门牌号就是端口号。
端口号分类
每个端口号唯一对应一个端口。但端口可以供不同的程序使用。程序运行时都要绑定一个端口,占用并使用一个端口号。
知名端口号
知名端口号是指众所周知的端口号,范围从0到1023。
这些端口号一般固定分配给一些系统服务进程,比如21端口分配给FTP(文件传输协议)服务,25端口分配给SMTP(简单邮件传输协议)服务,80端口分配给HTTP服务。
动态端口号
一般程序员开发应用程序可以使用的端口号称为动态端口号, 范围是从1024到65535。
如果程序员开发的程序没有特意设置端口号,操作系统会在动态端口号的范围内随机生成一个端口号给开发的应用程序使用。
运行一个程序默认会有一个端口号,当这个程序退出时,所占用的这个端口号就会被释放,可以供其他程序使用。如果没有特殊的端口绑定操作的话,下次不一定就是这个。
TCP协议
前面我们已经学习了
ip地址 网络设备 端口号 端口
可通过ip地址和端口号凯定位某个特定进程
进程之间进行数据传输,这是两者之间的事情。
我们需要一个协议,来确保数据传输能够按照某一规则成功进行。
TCP协议就是其中之一。
概念
TCP协议 (Transmission Control Protocol),即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
面向连接:TCP协议要求数据传输的双方进程只有在两者的端口之间建立有效连接后,才能进行数据传输。
可靠:字面意思。
基于字节流:可以传输大量二进制数据。
TCP通讯步骤
- 建立连接
- 传输数据
- 断开连接
特点
- 面向连接
- 通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接,以释放系统资源。
- 可靠传输
- TCP 采用发送应答机制(三次握手建立连接,四次握手断开连接)
- 超时重传
- 错误校验
- 流量控制和阻塞管理
socket套接字
实现数据传输,需要一个工具,它可以与端口绑定,负责数据传输的一切事务。
它就是socket套接字(英文意思为插座)。
socket负责实现进程之间的网络数据传输,好比数据的搬运工。
socket聚合了一系列与数据传输有关的函数
- connect()
- bind()
- listen()
- accept()
- send()
- recv()
- close()
TCP客户端程序开发
步骤
- 创建客户端程序的套接字对象
- 和服务端程序建立连接
- 发送数据
- 接收数据
- 关闭客户端程序的套接字
1:创建客户端程序的套接字对象
socket模块
socket模块是python内置的处理socket的类
导入
import socket
socket类
socket对象创建
client_socket=socket.socket(AddressFamily, Type)
-
AddressFamily
代表ip地址类型
常量名 | 含义 |
socket.AF_INET | IPv4类型 |
socket.AF_INET6 | IPv6类型 |
目前一般选择socket.AF_INET
-
Type
代表socket
类型
常量名 | 含义 |
socket.SOCK_STREAM | 面向连接 |
sokect.SOCK_DGRAM | 非面向连接 |
TCP协议下的程序选择socket.SOCK_STREAM
- 返回socket套接字对象
- 这里用
client_socket
接收
所以最常见的这行代码是:
client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
2:和服务端程序建立连接
connect方法
client_socket.connect(_address)
- _address是一个元组,格式为(host,port)。host是服务器ip地址(字符串),port是端口号。
- connect方法可以与ip地址为host,端口为port的服务端程序建立连接
如:
client_socket.connect(("192.168.147.1",8080))
3:发送数据
TCP协议要求只能发送二进制数据。
字符串编码
直接发送字符串是不行的,需要将字符串编码为二进制字节数据。
encode()
byte_str=string.encode(encoding)
- encoding代表编码方式,为字符串,常用的有"gbk“(汉字国标码),”UTF-8“(万国码)…
- 返回string以encoding方式编码后得到的字节流
send方法
client_socket.send(byte_str)
- 将
byte_str
传输到已经建立连接的服务端程序中
如:
send_data = "客户端发来消息...".encode("gbk")
client_socket.send(send_data)
通过网络调试助手看到的结果:
4:接收数据
recv方法
recv_data=client_socket.recv(_bufsize)
- 返回从已经建立连接的服务端程序中接收到的字节流数据
- _bufsize为最大接受的字节大小
字符串解码
decode()
string=byte_str.decode(encoding)
- 返回byte_str以encoding方式进行解码后得到的字符串
如:
用网络调试助手充当服务端
recv_date=client_socket.recv(1024)
recv_date=recv_date.decode("gbk")
print(recv_date)
5:关闭客户端程序的套接字
close方法
client_socket.close()
- 关闭客户端的socket,断开连接
客户端程序代码汇总:
import socket # 导入socket库
if __name__ == "__main__":
# 创建客户端程序的socket对象
# socket.AF_INET:ip地址类型为IPv4
# socket.SOCK_STREAM:面向连接
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端程序
# 192.168.147.1为服务端ip地址
# 8080为服务端程序的端口号
client_socket.connect(("192.168.147.1", 8080))
# 将字符串数据编码并发送至服务端程序
send_data = "客户端发来消息...".encode("gbk")
client_socket.send(send_data)
# 接收来自服务端程序的字节流数据并解码
recv_date = client_socket.recv(1024)
recv_date = recv_date.decode("gbk")
print(recv_date)
# 关闭客户端程序的socket,断开与服务端程序的连接
client_socket.close()
TCP服务端程序开发
步骤
- 创建服务端程序的套接字对象
- 绑定端口号
- 设置监听
- 等待接受客户端的连接请求
- 接收数据
- 发送数据
- 关闭服务端程序的套接字
1:创建服务端程序的套接字对象
import socket
server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- 语法同客户端程序
2:绑定端口号
服务端程序要让客户端程序能够找到,所以一般要要绑定一个固定的端口号
bind方法
server_socket.bind(_address)
- _address为元组,格式为(host, port)。host是服务端本地ip地址,一般设为
“”
,自动取。port为端口号,需指定以供客户端程序来连接。
如:
server_socket.bind(("", 8989))
设置端口号复用
服务端程序运行结束后,端口号并不会立即释放,需等待2-3分钟。我们可以在bind
方法前设置下面这行代码来设置端口号复用,即程序运行完立刻释放端口号。
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
- 将
socket.SO_REUSEADDR
设置为True
,即勾选端口复用选项
3:设置监听
客户端程序需要一直监听,直到接收到了客户端程序发来的连接请求。
listen()方法
server_socket.listen(_backlog)
-
_backlog
为最大监听数,即最多可以同时收到_backlog
个客户端程序的连接请求,并进行等待。server_socket在每个线程中只能同时连接一个客户端程序,其他被监听的客户端程序将被阻塞,排队等待连接。当申请建立连接的客户端程序超过最大监听数时,若还有客户端程序要发出连接请求,那么这个客户端程序连排队的机会都没有,直接报错。
如:
server_socket.listen(1024)
- 代表最多可以同时监听1024个客户端程序。
4:等待接受客户端程序的连接请求
服务端程序要一直等待监听。如果有客户端程序发来连接请求,服务端程序可以接受并处理。
accept()方法
new_server_socket,client_address=server_socket.accept()
- 没有客户端程序发来连接请求,则停在该行代码不动。
- 有客户端发来连接请求,就接受请求,并建立连接。
- 返回一个新的socket对象,专门与进行连接的客户端程序进行数据传输。这里用
new_server_socket
接收 - 以元组形式返回建立连接的客户端程序的ip地址和端口号。这里用
client_address
接收。
5:接收数据
语法同客户端程序,不过要用accept
返回的新套接字来处理。
6:发送数据
语法同客户端程序,不过要用accept
返回的新套接字来处理。
7:关闭服务端程序的套接字
两个套接字都要记得关闭。
new_server_socket.close()
server_socket.close()
- 关闭
new_server_socket
意味着对应的这个客户端程序和服务端程序断开连接。服务端程序还可以继续与其他新的客户端程序连接。 - 关闭
server_socket
意味着服务端程序的套接字关闭,不再能与新的客户端程序连接,但之前的连接还保留。
服务端程序代码汇总:
import socket
if __name__ == '__main__':
# 创建服务端程序的socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 给程序绑定端口号
server_socket.bind(("", 8989))
# 设置监听
# 128:最大等待建立连接的个数
server_socket.listen(128)
# 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 1. 专门用于和客户端程序通信的套接字: new_server_socket
# 2. 客户端的ip地址和端口号: client_address
new_server_socket, client_address = server_socket.accept()
print("客户端程序的ip地址和端口号:", client_address)
# 接收客户端程序发送的字节流数据并解码为字符串
recv_data = new_server_socket.recv(1024)
recv_data = recv_data.decode("gbk")
print(recv_data)
# 将字符串编码得到的字节流传输给客户端程序
send_data = "客户端发来消息...".encode("gbk")
new_server_socket.send(send_data)
# 关闭服务端程序与客户端程序传输专用的套接字
new_server_socket.close()
# 关闭服务端程序的套接字,结束向新的客户端程序提供连接机会
server_socket.close()
多任务版服务端程序
通过多线程的方式处理多个客户端程序的数据传输请求。
import socket
import threading
def handle(handle_socket, address):
"""与客户端程序进行数据传输并处理"""
while True:
recv_data = handle_socket.recv(1024)
if recv_data: # 对收到的消息进行处理
print(recv_data.decode("gbk"), address, sep=" from ")
handle_socket.send("OK,数据处理中...".encode("gbk"))
else: # 收到的消息为空
print("客户端程序已退出...", address, sep=" from ")
break
# 终止和客户端程序进行通信
handle_socket.close()
if __name__ == '__main__':
# 创建服务端套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
server_socket.bind(("", 8080))
# 设置监听
server_socket.listen(128)
# 循环等待接收客户端程序的连接请求
while True:
server_client_socket, client_address = server_socket.accept()
print("已成功连接客户端程序...", client_address, sep=" from ")
# 每当客户端和服务端建立连接成功以后,创建一个子线程,不同子线程负责处理不同客户端程序的消息
sub_thread = threading.Thread(target=handle, args=(server_client_socket, client_address))
# 设置守护主线程
sub_thread.Daemon = True
# 启动子线程
sub_thread.start()
# 服务端程序套接字可以不关闭,因为服务端程序需要一直运行