网络编程(1)
概念
局域网
- 同一局域网内的机器由交换机负责通信。
- 交换机只识别mac地址,交换机可以完成广播、单播、组播。
- arp协议:通过一台机器的IP地址获取到它的mac地址,用到交换机的广播和单播。
一台计算机想向同一局域网内的另一个计算机通信必须通过交换机,先向交换机发送自己的IP和mac地址,交换机通过广播向所有计算机发送信息,只有对应计算机会应答并向交换机发送自己的mac地址,从而实现同一局域网内的通信。
局域网之间通信
- 局域网之间通信通过路由器。
- 路由器可识别IP地址,并且路由器提供网关IP,同一个局域网的所有机器共享一个网关IP。
- 每个局域网有一个网段,路由器内有路由表,一个网关IP对应一个网段。
IP地址
- IPV4:点分十进制(0.0.0.0 - 255.255.255.255)
- 公网地址:需要申请购买
- 内网地址:保留字段
- 192.168.0.0 - 192.168.255.255 学校
- 172.16.0.0 - 172.31.255.255 学校
- 10.0.0.0 - 10.255.255.255 公司
- 127.0.0.1为本地回环地址,通常用于测试。
- 查看自己的IP地址:ipconfig(Windows)/ifconfig(Mac/Linix)
- 子网掩码:为一个IP地址,用于判断两台机器在不在同一局域网内,采用二进制相与的方法,当相与结果相同则在同一局域网内。
网络开发架构
C/S架构
-
需要安装才可使用
-
client:客户端,server:服务端
-
离线可使用,功能更完善,安全性更高
# 基于tcp # server端 import socket sk = socket.socket() sk.bind(('127.0.0.1', 5002)) # 5002是端口 # 打开监听模式 sk.listen() # 建立连接,conn是连接,addr是连接的地址 conn, addr = sk.accept() # 发送信息 conn.send(b'Hello, I am server') # 最多接收1024字节 msg = conn.recv(1024) print(msg) print(addr) conn.close() # 关闭连接 sk.close() # 关闭整个服务 ######################################### # client端 import socket sk = socket.socket() # 建立连接 sk.connect(('127.0.0.1', 5002)) # 接收信息 msg = sk.recv(1024) print(msg) # 发送信息 sk.send(b'Hello, I am client') sk.close() # 关闭连接
B/S架构
- 不用安装就可使用
- browser:浏览器,server:服务端
- 其中B/S也是C/S架构的一种
OSI五层协议
- 应用层:应用程序(python)
- 传输层:port,udp/tcp,四层路由器,四层交换机
- 网络层:ipv4/ipv6,路由器,三层交换机
- 数据链路层:mac,arp协议,网卡,二层交换机
- 物理层
tcp和udp
-
tcp:需要建立连接才能通信
- 占用连接,可靠(信息不会丢失),实时性高
- 建立连接:三次握手(建立全双工通信)
- 断开连接:四次挥手
-
udp:不需要建立连接就可以通信
-
不占用连接,不可靠(消息因为网络不稳定丢失)
-
需要用到
.recvfrom
(可接收信息和IP)和.sendto
(发送信息需附带IP地址)。# server端 # 一服务端对多客户端通信 # 服务端不用主动退出,由客户端收发消息进行退出 import socket sk = socket.socket(type = socket.SOCK_DGRAM) # 默认参数是tcp,此参数为udp sk.bind(('127.0.0.1', 5002)) while True: # 不能先发送,因为不知道接收端地址信息,只能等待接收,并且必须接收到客户端的地址 msg_r, addr = sk.recvfrom(1024) print(addr) print(msg_r.decode('UTF-8')) msg_s = input(">>>") sk.sendto(msg_s.encode('UTF-8'), addr) ################################################ # client端 import socket sk = socket.socket(type = socket.SOCK_DGRAM) # 传入服务端地址 server = ('127.0.0.1', 5002) while True: msg_s = input(">>>") sk.sendto(msg_s.encode('UTF-8'), server) if msg_s == "拜拜": break # 接收服务端的信息,由于知道服务端地址所以不需要recvfrom msg_r = sk.recv(1024) if msg_r.decode('UTF-8') == "拜拜": break print(msg_r.decode('UTF-8'))
-
-
粘包现象
-
只出现在tcp协议中,因为tcp协议的多条信息之间没有边界,并且有优化算法。
tcp协议发送数据大小没有上限,当数据过大时会将数据进行拆分,所以多条消息之间没有边界 -
发送端:两条消息都很短,发送间隔时间也非常短导致。
-
接收端:多条消息由于没有及时接收,接收端缓存堆在一起导致。
-
解决办法:设置边界
自定义协议:发送端统计长度,每次将长度固定为n字节发送
# server端同时发送两条消息 msg_s1 = input(">>>") msg_s2 =input(">>>") lens = str(len(msg_s1)) # 统计msg_s1的长度,ziff传入数据应为str,所以要转成str len = lens.zfill(4) # 将收集到的长度大小统一扩至4字节,例如msg_s1长度为6字节,则为0006 conn.send(len.encode('UTF-8')) # 将长度信息发送出去 conn.send(msg_s1.encode('UTF-8')) conn.send(msg_s2.encode('UTF-8')) # clien端接收 len = int(sk.recv(4).decode('UTF-8')) # 接收长度信息 msg_r1 = sk.recv(len) msg_r2 = sk.recv(1024) print(msg_r2.decode('UTF-8')) print(msg_r1.decode('UTF-8')) ############################################################### # struct方法 # server端同时发送两条消息 msg_s1 = input(">>>") msg_s2 =input(">>>") lens_byte = struct.pack('i', len(msg_s1)) # 可以将数据转成固定4字节 conn.send(lens_byte) conn.send(msg_s1.encode('UTF-8')) conn.send(msg_s2.encode('UTF-8')) # clien端接收 lens_byte = sk.recv(4) len = struct.unpack('i', lens_byte)[0] # unpack返回值为元组,第一位即为长度信息 msg_r1 = sk.recv(len) msg_r2 = sk.recv(1024) print(msg_r1.decode('UTF-8')) print(msg_r2.decode('UTF-8'))
-