1 网络编程概念
1)基本概念
-
机器的地址
- mac地址:能够唯一标示你这台机器的
- arp协议(通过ip找mac):地址解析协议,通过一台机器的ip地址获取到他的mac地址,用到了交换机的 广播和单播
- ip地址:能够更好的更方便的找到你的机器
- mac地址:能够唯一标示你这台机器的
-
局域网
- 单个局域网内的机器通信
- 网段
- 交换机:只认识mac地址
- 不能理解ip地址,只能理解mac地址
- 多个局域网通信
- 网关
- 路由器
- 可以理解ip地址
- 单个局域网内的机器通信
-
ip地址
-
一台机器的临时地址,能够更好更方便的找到你的机器地址
-
ipv4协议:四位点分十进制
- 0.0.0.0-255.255.255.255
-
ipv6协议
- 0:0:0:0:0:0 - FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF
-
公网地址:需要我们自己申请购买的地址
-
内网地址:保留字段,下面都是内网地址
- 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/ifconfig
-
-
子网掩码
-
也是一个ip地址,用来判断两台机器在不在一个局域网内
-
IP : 192.168.12.1 11000000.00011100.00001111.00000001 子网掩码:255.255.255.0 11111111.11111111.11111111.00000000 进行和运算 11000000.00011100.00001111.00000000 192.168.12.0
-
-
端口 port
- 用来确定一台机器的具体应用
- 虚拟单位,不是实际存在
- 0-65535
- 个人电脑发送请求时操作系统随机分配,服务器端有固定端口
-
osi 7层协议(osi 五层协议是将前三层合并)
- 协议:
- 内容(osi五层协议)
- 应用层(5层)
- python代码
- 传输层
- 预备如何传输,使用的端口port(tcp、udp协议) b’端口+内容’
- 机器:四层路由器–>四层交换机
- 网络层
- 使用的ip(ivp4 ivp6) b’ip+端口+内容’
- 机器:路由器–>三层交换机
- 数据链路层
- 使用的mac地址(arp协议) b’mac地址+ip+端口+内容’
- 机器:网卡–>二层交换机
- 物理层(1层)
- 物理线路
- 接收时刚好相反
- 应用层(5层)
-
tcp 和 udp 协议
-
tcp协议(打电话) - 线下缓存高清电影/qq远程控制/发邮件
-
先建立连接,才能通信
-
占用连接/可靠(消息不丢失)/实时性高/慢
-
建立连接:三次握手
-
连接过程中:收发消息,两次握手
-
断开连接:四次挥手
-
-
udp协议(发短信) - 在线播放视频/qq微信消息
- 不需要建立连接,就可以通信
- 不占用连接/不可靠(网络不稳定时会丢失)/实时性底/快
-
2)应用-最简单的网络通信
-
B/S 架构和 C/S 架构
- C/S架构(需要安装)
- client 客户端
- server 服务端
- B/S架构
- browser 浏览器
- server 服务端
- C/S 和 B/S 的关系
- B/S 架构也是 C/S 架构的一种
- C/S 架构的优点
- 可以离线使用
- 功能更完善
- 安全性更高
- B/S 架构的优点
- 不用安装
- 统一PC端用户的入口
- C/S架构(需要安装)
-
socket模块实现简单网络通信测试
# server.py-->服务器端 import socket sk = socket.socket() # 创建一个server端对象 sk.bind(('127.0.0.1', 9000)) # 给server端绑定一个地址 sk.listen() # 开始监听(可以接收)客户端给我的连接了 conn, addr = sk.accept() # 建立连接 conn.send(b'hello') msg = conn.recv(1024) print(msg) conn.close() # 关闭连接 sk.close()
# client.py-->客户端
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) # 建立连接
msg = sk.recv(1024) # 接收消息
print(msg)
sk.send(b'byebye') # 传输消息
sk.close() # 断开连接
'''
测试时先打开服务器端,再打开客户端进行连接
'''
2 TCP协议和UDP协议进阶(网络编程)
1)TCP协议和UDP协议基于socket模块实现
-
TCP协议
- 语法:
- socket(type=socket.SOCK_STRAM) tcp协议的server,默认参数可以不用传
- bind() 绑定一个ip和端口
- listen() 监听,代表socket服务的开启
- accept() 等到有客户端来访问和客户端建立连接
- send() 直接通过连接发送消息,不需要写地址
- recv() 只接收消息
- connect() 客户端/tcp协议的方法,和server端建立连接
- close() 关闭服务/连接
- 服务端和多个客户端进行通信
- 服务器端和客户端进行多次通话
- 实例代码如下:
# server.py 服务器端 import socket sk = socket.socket() sk.bind(('127.0.0.1',9001)) # 申请操作系统的资源 sk.listen() 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': break msg = conn.recv(1024).decode('utf-8') if msg.upper() == 'Q': break print(msg) conn.close() # 挥手 断开连接 sk.close() # 归还申请的操作系统的资源
# client.py 客户端 import socket sk = socket.socket(type = socket.SOCK_STREAM) # stream 数据流 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()
- 语法:
-
UDP协议
- 语法:
- socket.socket(type=socket.SOCK_DGRAM) dgram->datagram 数据包
- sendto() 需要写一个对方的地址
- recvfrom() 接收消息和地址
- close() 关闭服务/连接
- 服务器端和客户端进行通信
# server.py 服务器端 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)
# client.py 客户端 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)
- 识别不同客户端
- 语法:
2)粘包现象
- 现象:两条或更多条连续发送的消息粘在一起
-
粘包发生特点
- 只出现在tcp协议中,因为tcp协议多条消息之间没有边界,并且还有一大堆优化算法
- tcp协议多条消息之间没有边界:因为tcp协议和udp协议传输数据时会有最大带宽限制MKT(一般MKT=1500字节),但是tcp协议可以通过Python更改最大带宽限制,从而一次传输大量数据,但是传输数据时没有边界,导致接收后出现粘包现象
-
原因:
- 发送端发送消息时传输时间间隔很短
- 接收端没有及时接收消息,而在对方的缓存中堆积在一起造成的
-
粘包发生的本质:tcp协议是流式传输,数据与数据之间没有边界
-
解决方法
-
自定义协议:利用设置边界解决粘包现象
-
规定最大传输长度为n字节,每次接收时都接受n字节的数据作为一个数据
-
发送端:
- 第一种:计算将要发送的数据长度,通过struct模块将长度转换为固定的n字节,发送n个字节的长度
- 第二种:发送的数据相关的内容组成json:先发json的长度,再发json,json中存了接下来要发送的数据长度,再发数据(文件传输代码中实现了)
-
接收端:接收n字节,再使用struct.unpack将n字节转换为数字,这个数字就是将要接受的数据的长度,再根据长度接收数据,两条数据就不会粘在一起了
-
-
struct模块
- struct.pack(‘i’, 字符串)
- struct.unpack(‘i’, 字符串)
-
实例代码
# server.py 服务器端 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() # '10001' # ret = num.zfill(4) # '0006' # 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()
# client.py 客户端 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()
-
3)文件上传和下载代码
# server端
import socket
import struct
import time
login_state = {
'name' : None,
'state': False
}
sk = socket.socket()
sk.bind(('127.0.0.1', 9006))
sk.listen()
conn, addr = sk.accept()
msg = conn.recv(1024).decode('utf-8')
with open('register.txt', encoding='utf-8', mode='r') as f:
for i in f:
if msg == i.strip():
conn.send('登陆成功'.encode('utf-8'))
login_state['name'] = msg.split('|')[0]
login_state['state'] = True
if login_state['state'] == False:
conn.send('q'.encode('utf-8'))
while True:
if login_state['state'] == True:
conn.send('1上传/2下载:'.encode('utf-8'))
opt = conn.recv(1024).decode('utf-8')
if opt == '1':
dir_test = conn.recv(1024).decode('utf-8').strip().split('/')[-1]
print(dir_test)
while True:
with open('../updateTest/' + dir_test, encoding='utf-8', mode='a') as f:
msg = conn.recv(1024).decode('utf-8')
if msg.upper() == 'Q':
break
f.write(msg)
break
elif opt == '2':
file_dir = conn.recv(1024).decode('utf-8')
with open(file_dir, encoding='utf-8', mode='r') as f:
for i in f:
i = i.strip()
print('传输中')
# length = struct.pack('i', len(i))
# conn.send(length)
conn.send(i.encode('utf-8'))
time.sleep(1)
print('传输完成')
time.sleep(2)
conn.send('q'.encode('utf-8'))
break
else:
login_state['name'] = None
login_state['state'] = False
break
else:
break
conn.close()
sk.close()
# client端
import socket
import struct
import time
sk = socket.socket()
sk.connect(('127.0.0.1', 9006))
msg1 = input('name>>>')
msg2 = input('pwd>>>')
msg = msg1.strip