python - 网络编程
★定义
由于不同机器上的程序要通信,才产生了网络
◇C/S架构
server服务端
client客户端
◇B/S架构
broswer浏览端
server客户端
如:各种小程序和公众号
★通信
◇网线
连接每一台机器的桥梁
◇网卡
每台计算机有全球唯一的mac地址(物理地址)
◇ARP协议
通过IP地址找到对应的MAC地址
◇交换机
多台机器之间通信的问题
◇网关
局域网中的机器想要访问局域网外的机器,需要网关
◇网段
IP地址和子网掩码按位【与运算】
◇IP协议
- 127.0.0.1 本地回环地址
- 为每一台计算机分配IP地址
- 确定哪些地址在同一个子网络
◇通信协议
TCP协议
全双工,双方都可以收发信息
建立通信的过程
1,建立连接:三次握手
2,数据传输:长链接
3,断开连接:四次挥手
UDP协议
是一个简单的传输协议,只把数据发出去,但不能保证它们能到达目的地
◇端口
在计算机上,每一个网络通信的程序,都会开一个端口
端口范围:0-65535
一般情况下我们用:8000之后的端口
◆IP和端口的区别
IP:确定唯一的一台机器
端口:确定唯一的一个程序
★五层模型
◇应用层
协议:
- HTTP协议
- HTTPS协议
- 花钱,可靠
- FTP传输协议
- SMTP协议
◇传输层
选择通信协议
- TCP
- UDP
◇网络层
给上一层的信息加上IP信息
IP协议
◇数据链路层
给上一层的信息加上MAC地址
ARP协议
◇物理层
网线网卡
传输比特流0101010
★socket模块
只要确定了IP和Port,就能使用socket来与之通信
◇类型
- 基于文件类型:AF_UNIX
- 基于网络类型:AF_INEF
◇socket实例
基于TCP方式(会黏包)
1 import socket 2 3 sk = socket.socket() # 实例socket对象 4 sk.bind(('127.0.0.1', 8080)) # 绑定ip和端口 5 sk.listen() # 监听 6 7 conn, addr = sk.accept() # 建立连接 8 conn.send(b'hello') # 发送消息 9 res = conn.recv(1024).decode('utf-8') # 接收消息 10 print(res) 11 conn.close() # 关闭连接 12 13 sk.close() # 关闭socket对象
1 import socket 2 3 sk = socket.socket() # 创建socket对象 4 sk.connect(('127.0.0.1', 8080)) # 创建连接 5 6 res = sk.recv(1024).decode('utf-8') # 接收信息 7 print(res) 8 sk.send(b'ok') # 发送信息 9 sk.close() # 关闭连接
基于UDP方式(会丢包)
1 import socket 2 3 sk = socket.socket(type=socket.SOCK_DGRAM) # 创建一个基于UDP协议的socket对象 4 sk.bind(('127.0.0.1', 8080)) # 绑定IP和port 5 6 ret, addr = sk.recvfrom(1024) # 接收消息 并接收一个 addr 7 print(ret.decode('utf-8')) 8 sk.sendto(b'hello',addr) # 发送消息 需要传addr 9 10 sk.close() # 关闭socket对象
1 import socket 2 3 sk = socket.socket(type=socket.SOCK_DGRAM) # 创建一个基于UDP协议的socket对象 4 addr = ('127.0.0.1', 8080) 5 6 sk.sendto(b'hi', addr) 7 ret,addr = sk.recvfrom(1024) # 接收消息 并接收一个 addr 8 print(ret.decode('utf-8')) # 发送消息 需要传addr 9 10 sk.close() # 关闭socket对象
◇黏包(只在TCP协议中)
原因:面向连接的流传输,数据是无边界的
1,缓冲机制:不知道客户端发送的数据的长度,recv接收不完的数据,会缓存在内存里,等待下一个recv
2,优化算法:连续的小数据包会被合并在一起发送,会被一个recv接收
解决办法
发送大的数据前,告诉接收端我要传多大的数据,接收端接收合适的数据大小
通过struct模块
固定长度的bytes
pack:就是即将把一个数据转换成固定长度的bytes类型
unpack:unpack之后拿到的数据是一个元祖
1 import socket 2 import struct 3 4 sk = socket.socket() 5 sk.bind(('127.0.0.1', 8090)) 6 sk.listen() 7 8 conn, addr = sk.accept() 9 while True: 10 cmd = input('>>>') 11 if cmd == 'q': 12 break 13 conn.send(cmd.encode('gbk')) # 发命令 14 num = conn.recv(4) 15 num = struct.unpack('i', num)[0] # unpack之后拿到的数据是一个元祖 16 ret = conn.recv(num).decode('gbk') # 接收信息 17 print(ret) 18 19 20 conn.close() 21 sk.close()
1 import socket 2 import subprocess 3 import struct 4 5 sk = socket.socket() 6 sk.connect(('127.0.0.1', 8090)) 7 8 while True: 9 cmd = sk.recv(1024).decode('gbk') # 接收一个命令 10 if cmd == 'q': 11 break 12 res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 在本地执行命令 13 stdout_ret = res.stdout.read() # gbk字节 14 stderr_ret = res.stderr.read() # gbk字节 15 num = len(stdout_ret) + len(stderr_ret) 16 sk.send(struct.pack('i', num)) # 把数据包的大小传过去(这个数据包本身4字节) 17 sk.send(stdout_ret) # 将命令结果发给服务端 18 sk.send(stderr_ret) # 将命令结果发给服务端 19 20 sk.close()