更
多
内
容
持
续更
新
中
.
.
.
.
.
.
点 击 上 方 蓝 字 加 关 注 哦 ! ! !
1. 网络开发架构
-
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()
-
socket参数的详解
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
创建socket对象的参数说明:
family 地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域实际上是使用本地 socket 文件来通信)type 套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。
SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。proto 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。 fileno 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。
与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。
这可能有助于使用socket.close()关闭一个独立的插座。 -
B/S架构 : 百度 博客园 谷歌 码云
-
browser 浏览器
-
server 服务端
-
-
b/s和c/s什么关系?
-
B/S架构也是C/S架构中的一种
-
C / S架构的好处
可以离线使用/功能更完善/安全性更高
B / S架构
不用安装就可以使用
统一PC端用户的入口
2. osi5层协议
应用层 python 【包括:表示层 会话层】 # 5 层
传输层 port udp tcp 四层路由器 四层交换机 # 4
网络层 ip协议(ipv4 ipv6) 路由器 三层交换机 # 3
数据链路层 mac arp协议 网卡 二层交换机 # 2
物理层 # 1
人们按照分工不同把互联网协议从逻辑上划分了层级:
socket层
3. 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)
udp协议
-
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)
好了小伙伴们,这就差不多python中网络编程要掌握的知识,如果有兴趣的话,可以自己去看看,然后一起交流。
- END -