网络编程小结
目录
软件开发架构
C/S架构
client:客户端
server:服务端
优点:软件的使用稳定,网络资源占用少
缺点:
- 若需要使用多个软件,需要下载多个客户端
- 服务端更新后,用户也需要跟着下载更新
B/S架构
browser:浏览器
server:服务端
优点:以浏览器充当客户端,服务端更新不需要用户更新下载
缺点: 占用网络资源大,网络不好时,体验差
互联网OSI七层协议
从下往上分为:
物理层--> 数据链路层--> 网络层--> 传输层--> 会话层--> 表示层--> 应用层
物理层
传输电信号 0100011
数据链路层
-
规定好电信号的分组方式
-
必须要由一块网卡
-
mac地址:12位唯一的16进制数
前6位是厂商号
后6位是流水号
-
以太网协议:
- 在同一个局域网内通信
- 单播 1对1吼
- 广播 多对多吼
- 会有广播风暴, 不能对局域网通信
- 在同一个局域网内通信
网络层
ip:定位局域网位置
port: 端口,唯一标识一台计算机上的一个程序
arp协议:将mac地址获取,并解析成ipi地址
传输层
TCP协议
TCP协议称之为流式协议
若想要通信,必须建立连接,并建立双向通道
三次握手,四次挥手:
- 三次握手建立连接
- 客户端往服务端发送建立通道
- 服务端要确认客户端的请求,并往客户端也发送请求建立通道
- 客户端接收到服务端建立连接的请求,并返回确认
- 建立双向通道
- 双向通道
- 反馈机制
- 客户端往服务端发送请求获取数据,服务端务必返回数据,客户端确认收到,否则会反复发送,一直到某个时间段内,会停止发送
- 四次挥手断连接
- 客户端往服务端发送断开连接请求,服务端返回确认收到
- 服务端需要再次发送断开连接请求
- 客户端返回确认收到
- 最终确认断开来连接
UDP协议
- 数据不安全
- 不需要建立双向通道
- 传输速度快
- 不会由粘包问题
- 客户端发送数据,不需要服务端确认收到
应用层
ftp、http
socket套接字
socket用来写套接字客户端与服务端的模块,内部帮我们封装好了7层协议需要做的事情.
socket套接字模板
# 服务端
import socket
server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5)
while True:
conn, addr = server.accept()
print(addr)
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
continue
print(data.decode('utf-8'))
conn.send(data)
except Exception as e:
print(e)
break
conn.close()
# 客户端
import socket
client = socket.socket()
client.connect(
('127.0.0.1', 9527)
)
while True:
send_msg = input('客户端:')
client.send(send_msg.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))
subprocess
用来通过代码往cmd创建一个管道,并且发送命令和接收cmd返回的结果
# 伪代码:
import subprocess
obj = subprocess.Popen(
'命令',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
msg = obj.stdout.read() + obj.stderr.read()
粘包问题
- 不能确定对方发送数据的大小
- 在短时间内,间隔时间短,并且数据量小的情况下,默认将这些数据打包成一个多次发送的数据,然后一次性发送
struct解决粘包问题
可以将一个数据的长度打包成一个固定长度的报头.
struct.pack('模式i', '源数据长度')
data = 'gagawagwaga'
# 打包成报头
headers = struct.pack('i', len(data))
# 解包获取数据真实长度
data = struct.unpack('i', headers)[0]
# 服务端
import socket
import subprocess
import struct
server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5)
while True:
conn, addr = server.accept()
print(addr)
while True:
try:
cmd = conn.recv(1024).decode('utf-8')
if len(cmd) == 0:
continue
obj = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res = obj.stdout.read() + obj.stderr.read()
headers = struct.pack('i', len(res))
conn.send(headers)
conn.send(res)
except Exception as e:
print(e)
break
conn.close()
# 客户端
import socket
import struct
client = socket.socket()
client.connect(
('127.0.0.1', 9527)
)
while True:
cmd = input('客户端输入命令:')
client.send(cmd.encode('utf-8'))
headers = client.recv(4)
data_len = struct.unpack('i', headers)[0]
data= client.recv(data_len)
print(data.decode('gbk'))
升级版: 先将数据存放到字典中,将字典打包发送过去
字典的好处:真实数据长度;文件的描述信息;发送的数据更小
socketserver
# 服务端
import socketserver
class MyTCPServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.client_address)
while True:
try:
data = self.request.recv(1024).decode('utf-8')
send_msg = data.upper()
self.request.send(send_msg.encode('utf-8'))
except Exception as e:
print(e)
break
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(
('127.0.0.1', 9527), MyTCPServer
)
server.serve_forever()