python-网络编程
第十章 网络编程
1. 网络是什么?
-
-
功能:信息的传输与共享和完成一些简单的程序。
-
类别:网络操作系统。
-
发展:早期 联机 ----> 以太网 局域网与交换机
2. 广播
-
主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。
3. ip地址与ip协议
-
规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,广泛采用的v4版本即ipv4,它规定网络地址由32位2进制表示
-
范围0.0.0.0-255.255.255.255
-
变化的 : ip地址 能够更好的更方便的找到你的机器
-
一个ip地址通常写成四段点分十进制数,例:172.16.10.1
-
公网地址 :需要我们自己申请购买的地址
-
内网地址 :保留字段
192.168.0.0 - 192.168.255.255 学校
172.16.0.0 - 172.31.255.255 学校
10.0.0.0 - 10.255.255.255 公司
-
特殊的ip地址
-
127.0.0.1 本地回环地址 测试的时候用的
-
-
查看自己的ip地址 ipconfig【在windows系统里cmd】/ifconfig【在linux上】
-
子网掩码也是一个ip地址 用来判断两台机器在不在一个局域网内
比如,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算,
172.16.10.1:10101100.00010000.00001010.000000001
255255.255.255.0:11111111.11111111.11111111.00000000
AND运算得网络地址结果:10101100.00010000.00001010.00000000->172.16.10.0
172.16.10.2:10101100.00010000.00001010.00000010
255255.255.255.0:11111111.11111111.11111111.00000000
AND运算得网络地址结果:10101100.00010000.00001010.00000000->172.16.10.0
结果都是172.16.10.0,因此它们在同一个子网络。
-
IPV6协议:现在较少用,能表示的ip地址更多
范围:0:0:0:0:0:0 - FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF
4. mac地址
-
ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。
-
每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)
-
不变的 : mac地址 能够唯一标识你这台机器的【vnc/飞秋 都不涉及mac】
-
arp协议 ——查询IP地址和MAC地址的对应关系
-
通过ip找mac
-
地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。
-
5. 局域网
-
局域网的概念
-
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。
-
交换机:在同一个局域网内的机器由交换机负责通信。
-
只能解析机器的mac地址,不能解析ip地址
-
可以完成广播,组播,单播(单播--> mac地址(在网卡上))
-
-
-
局域网之间通信
-
路由器 :是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号。路由器具有判断网络地址和选择IP路径的功能,它能在多网络互联环境中,建立灵活的连接,可用完全不同的数据分组和介质访问方法连接各种子网,路由器只接受源站或其他路由器的信息,属网络层的一种互联设备。
-
可以解析ip地址
-
提供网关ip,同一个局域网的所有机器共享一个网关
-
我们不能访问除了本局域网之外的其他内网的IP地址
-
-
端口(port):用来确认一台机器上的具体应用程序(0~65535)
-
6. 网络开发架构
-
C/S架构:需要安装才能使用
-
client 客户端 我们用的 需要安装的
-
server 服务端
-
# server 服务端
import socket
sk = socket.socket() # 创建一个server端的对象
sk.bind(('127.0.0.1', 801)) # 给server端绑定一个地址 以元组的形式,('ip地址',端口)
sk.listen() # 开始监听(可以接收)客户端给我的连接了
conn,addr = sk.accept() # 建立连接 conn是连接
while 1:
mes = input('>>>')
conn.send(mes.encode('utf-8'))
msg = conn.recv(1024).decode('utf-8')
print(msg)
conn.close() # 关闭连接
sk.close()
# client 客户端
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 801))
while 1:
mse = input('>>>')
sk.send(mse.encode('utf-8'))
msg = sk.recv(1024).decode('utf-8')
print(msg)
sk.close()
-
B/S架构 : 百度 博客园 谷歌 码云
-
browser 浏览器
-
server 服务端
-
-
b/s和c/s什么关系?
-
B/S架构也是C/S架构中的一种
-
C/S架构的好处
可以离线使用/功能更完善/安全性更高
B/S架构
不用安装就可以使用
统一PC端用户的入口
7. osi5层协议
应用层 python 【包括:表示层 会话层】 # 5 层
传输层 port udp tcp 四层路由器 四层交换机 # 4
网络层 ip协议(ipv4 ipv6) 路由器 三层交换机 # 3
数据链路层 mac arp协议 网卡 二层交换机 # 2
物理层 # 1
8 tcp和udp
-
tcp(语音聊天/视频聊天) - 线下缓存高清电影\qq远程控制\发邮件
-
需要先建立连接 然后才能通信的
-
占用连接\可靠(消息不会丢失)\实时性高\速度慢
-
# 建立连接 - 三次握手
客户端向服务器端发送syn请求,
服务端向客户端回复ack并发送syn请求,
客户端接收到请求之后再回复ack表示建立连接
由客户端的connect + 服务端的accept
# 断开连接 - 四次挥手
客户端向服务端发送fin请求,
服务端回复ack确认
服务端向客户端发送fin请求,
客户端回复ack确认
有客户端的close和服务端的close
-
最简单的tcp网络通信
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9001)) # 申请操作系统的资源
sk.listen()
print('sk : ',sk)
conn,addr = sk.accept() # conn里存储的是一个客户端和server端的连接信息
print('conn : ',conn)
conn.send(b'hello')
msg = conn.recv(1024)
print(msg)
conn.close() # 挥手 断开连接
sk.close() # 归还申请的操作系统的资源
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
msg = sk.recv(1024)
print(msg)
sk.send(b'byebye')
sk.close()
tcp协议
-
在连接内多和客户说几句-代码
-
加whlie True 为了和多个客户端进行握手
-
sk代表申请的系统的资源
-
conn里存储的是一个客户端和server端的连接信息
tcp协议的多人多次通信
# 和一个人通信说多句话
# 和一个人聊完再和其他人聊
# socket() tcp协议的server
# bind 绑定一个ip和端口
# listen 监听,代表socket服务的开启
# accept 等,到有客户端来访问和客户端建立连接
# send 直接通过连接发送消息,不需要写地址
# recv 只接收消息
# connect 客户端/tcp协议的方法,和server端建立连接
# close 关闭服务/连接
**server**(服务端)
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9001)) # 申请操作系统的资源
sk.listen() # 监听 代表socket的开启
while True: # 为了和多个客户端进行握手
conn,addr = sk.accept() # 能够和多个客户端进行握手了
print('conn : ',conn)
while True: # 能够接受多个客户的请求
send_msg = input('>>>')
conn.send(send_msg.encode('utf-8'))
if send_msg.upper() == 'Q': # 服务端(server)断开和客户端(client)的连接
break
msg = conn.recv(1024).decode('utf-8')
if msg.upper() == 'Q': break # 客户端断开和服务端的连接
print(msg)
conn.close() # 挥手 断开连接
sk.close() # 归还申请的操作系统的资源
# str -encode('utf-8')-> bytes
# str -encode('gbk')-> bytes
# '你' 字符
# utf-8 b'181921' b'181921' -decode('utf-8')-> 你
# gbk b'17210' b'17210' -decode('gbk') -> 你
# 基于tcp协议的文件传输
**client**(客户端)
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
while True:
msg = sk.recv(1024)
msg2 = msg.decode('utf-8')
if msg2.upper() == 'Q':break
print(msg,msg2)
send_msg = input('>>>')
sk.send(send_msg.encode('utf-8'))
if send_msg.upper() == 'Q':
break
sk.close()
tcp协议的粘包现象
什么是粘包?
两条或更多条分开发送的信息连在一起就是粘包现象
粘包现象 只出现在tcp协议中,因为tcp协议 多条消息之间没有边界,并且还有一大堆优化算法 发送端 : 两条消息都很短,发送的间隔时间也非常短
发生在发送端 : 发送间隔短,数据小,由于优化机制就合并在一起发送了
接收端 : 多条消息由于没有及时接收,而在接收方的缓存短堆在一起导致的粘包
发生在接收端 : 接收不及时,所以数据就在接收方的缓存端黏在一起了
粘包发生的本质 : tcp协议的传输是流式传输 数据与数据之间没有边界
解决粘包问题的本质 :设置边界,可以借助struct模块
# 先发送四字节的数据长度 # 先接受4字节 知道数据的长度
# 再按照长度发送数据 # 再按照长度接收数据
tcp协议的自定义协议解决粘包问题 主要掌握逻辑和代码
# 1 recv(1024)不代表一定收到1024个字节,而是最多只能收这么多
# 2 两条连续发送的数据一定要避免粘包问题
# 3 先发送数据的长度 再发送数据
# 发送的数据相关的内容组成json:先发json的长度,再发json,json中存了接下来要发送的数据长度,再发数据
# 基于tcp上传(大)文件
# server端
import json
import struct
import socket
# 接收
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()
conn,_ =sk.accept()
msg_len = conn.recv(4)
dic_len = struct.unpack('i',msg_len)[0]
msg = conn.recv(dic_len).decode('utf-8')
msg = json.loads(msg)
with open(msg['filename'],'wb') as f:
while msg['filesize'] > 0:
content = conn.recv(1024)
msg['filesize'] -= len(content)
f.write(content)
conn.close()
sk.close()
# client端
import os
import json
import struct
import socket
# 发送
sk = socket.socket()
# sk.connect(('192.168.14.109',9012))
sk.connect(('127.0.0.1',9001))
# 文件名\文件大小
abs_path = r'D:\python22期\day28 课上视频\3.网络基础概念.mp4'
filename = os.path.basename(abs_path)
filesize = os.path.getsize(abs_path)
dic = {'filename':filename,'filesize':filesize}
str_dic = json.dumps(dic)
b_dic = str_dic.encode('utf-8')
mlen = struct.pack('i',len(b_dic))
sk.send(mlen) # 4个字节 表示字典转成字节之后的长度
sk.send(b_dic) # 具体的字典数据
with open(abs_path,mode = 'rb') as f:
while filesize>0:
content = f.read(1024)
filesize -= len(content)
sk.send(content)
sk.close()
struct模块:该模块可以把一个类型,如数字,转成固定长度的bytes【socket 底层模块】
>>> struct.pack('i',1111111111111)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
import struct
num1 = 129469649
num2 = 123
num3 = 8
ret1 = struct.pack('i',num1)
print(len(ret1))
ret2 = struct.pack('i',num2)
print(len(ret2))
ret3 = struct.pack('i',num3)
print(len(ret3))
print(struct.unpack('i',ret1)[0])
print(struct.unpack('i', ret2))
print(struct.unpack('i', ret3))
借助sturct可以完成自定义协议
import struct
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()
conn,addr = sk.accept()
msg1 = input('>>>').encode()
msg2 = input('>>>').encode()
# num = str(len(msg1)) # '10001'
# ret = num.zfill(4) # '0006' # 不借助struct模块也可以实现自定义协议
# conn.send(ret.encode('utf-8'))
blen = struct.pack('i',len(msg1))
conn.send(blen)
conn.send(msg1)
conn.send(msg2)
conn.close()
sk.close()
import time
import struct
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
# length = int(sk.recv(4).decode('utf-8'))
length = sk.recv(4)
length = struct.unpack('i',length)[0]
msg1 = sk.recv(length)
msg2 = sk.recv(1024)
print(msg1.decode('utf-8'))
print(msg2.decode('utf-8'))
sk.close()
# 每一句话什么意思?执行到哪儿程序会阻塞?为什么阻塞?什么时候结束阻塞?
# input() # 等待,直到用户输入enter键
# accept 阻塞,有客户端来和我建立完连接之后
# recv 阻塞,直到收到对方发过来的消息之后
# recvfrom 阻塞,直到收到对方发过来的消息之后
# connect 阻塞,直到server端结束了对一个client的服务,开始和当前client建立连接的时候
验证客户合法性【主要防止恶意攻击,提高安全性】
# 什么场景?
# 是在公司内部 无用户的情况下(没人能看得见我们的client端代码的时候),有人的情况下 就直接做登录即可。
# 密文登录
# server userinfo密文
# client input明文密码
# 至少要在server端进行一次摘要
import os
import socket
import hashlib
secret_key = b'alex_sb'
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()
conn,addr = sk.accept()
# 创建一个随机的字符串
rand = os.urandom(32)
# 发送随机字符串
conn.send(rand)
# 根据发送的字符串 + secrete key 进行摘要
sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()
# 等待接收客户端的摘要结果
res_client = conn.recv(1024).decode('utf-8')
# 做比对
if res_client == res:
print('是合法的客户端')
# 如果一致,就显示是合法的客户端
# 并可以继续操作
conn.send(b'hello')
else:
conn.close()
# 如果不一致,应立即关闭连接
import socket
import hashlib
secret_key = b'alex_sb979'
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
# 接收客户端发送的随机字符串
rand = sk.recv(32)
# 根据发送的字符串 + secret key 进行摘要
sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()
# 摘要结果发送回server端
sk.send(res.encode('utf-8'))
# 继续和server端进行通信
msg = sk.recv(1024)
print(msg)
socketserver模块【socketserver 基于socket完成的,可以模仿并发可以多用户同时访问】
import time
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
while True:
try:
content = conn.recv(1024).decode('utf-8')
conn.send(content.upper().encode('utf-8'))
time.sleep(0.5)
except ConnectionResetError:
break
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
# import socket
#
# sk = socket.socket()
# sk.bind(('127.0.0.1',9001))
# sk.listen()
# while True:
# conn,_ = sk.accept()
# while True:
# try:
# content = conn.recv(1024).decode('utf-8')
# conn.send(content.upper().encode('utf-8'))
# time.sleep(0.5)
# except ConnectionResetError:
# break
# class BaseRequestHandler:
# def __init__(self):
# self.handle()
# def handle(self):
# pass
#
# class Myserver(BaseRequestHandler):
# def handle(self):
# pass
# my = Myserver()
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
while True:
sk.send(b'hello')
content = sk.recv(1024).decode('utf-8')
print(content)
dp协议
-
udp(发消息) - 在线播放视频\qq发消息\微信消息
-
不需要建立连接 就可以通信的
-
不占用连接\不可靠(消息因为网络不稳定丢失)\速度快
-
udp不会发生粘包
-
udp协议的多人通信
# socket(type=socket.SOCK_DGRAM)
# sendto 需要写一个对方的地址
# recvfrom 接收消息和地址
# close 关闭服务/连接
import socket
sk = socket.socket(type = socket.SOCK_DGRAM) # 套接字
sk.bind(('127.0.0.1',9001))
while True:
msg,addr= sk.recvfrom(1024)
print(msg.decode('utf-8'))
msg = input('>>>')
sk.sendto(msg.encode('utf-8'),addr)
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
server = ('127.0.0.1',9001)
while True:
msg = input('>>>')
if msg.upper() == 'Q':break
sk.sendto(msg.encode('utf-8'),server)
msg = sk.recv(1024).decode('utf-8')
if msg.upper() == 'Q':break
print(msg)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)