python-网络编程
1.osi五层协议
# 应用层 # 传输层 # tcp协议 : 效率低 面向连接\可靠\全双工的通信 # 三次握手 # 客户端向服务器端发送syn请求, # 服务端向客户端回复ack并发送syn请求, # 客户端接收到请求之后再回复ack表示建立连接 # 由客户端的connect + 服务端的accept # 四次挥手 # 客户端向服务端发送fin请求, # 服务端回复ack确认 # 服务端向客户端发送fin请求, # 客户端回复ack确认 # 有客户端的close和服务端的close # udp协议 : 效率高 无连接的\不可靠 # 四层交换机 四层路由器 # 网络层 # ip协议(ipv4 ipv6) # 路由器\三层交换机 # 数据链路层 # arp协议 地址解析协议 通过ip找到mac地址 # 交换机\网卡 : 单播 广播 组播 # 物理层
2 b/s c/s架构
C/S client server
B/S browser server
3.tcp协议和udp协议
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
我知道说这些你们也不懂,直接上图。
3.简单的tcp网络通信
#server服务端 import socket #炒茄子 sk = socket.socket() # 创建一个server端的对象 sk.bind(('127.0.0.1',9001)) # 给server端绑定一个地址 ,可以写自己的IP地址 sk.listen() # 开始监听(可以接收)客户端给我的连接了 conn,addr = sk.accept() # 建立连接 conn是连接,ADDR是地址,也可以不写这个变量 conn.send(b'hello') #联上后,进行通信 msg = conn.recv(1024) #1024 表示最多接收1024个字 print(msg) conn.close() # 关闭连接 sk.close() #关掉服务
client客户端 import socket sk = socket.socket() #实例化对象 sk.connect(('127.0.0.1',9001)) #联接server端 msg = sk.recv(1024) #1024 表示最多接收1024个字 print(msg) sk.send(b'byebye') sk.close()
5.tcp简单多人通信
#server端 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 # _*_ coding : UTF-8 _*_ 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()
6.简单的udp协议
#server import socket sk = socket.socket(type= socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9000)) msg,addr = sk.recvfrom(1024) print(msg) sk.sendto(b'2',addr)
# client import socket sk = socket.socket(type=socket.SOCK_DGRAM) server = ('127.0.0.1',9000) sk.sendto(b'1',server) msg = sk.recv(1024) print(msg)
7.多人的udp协议
#server import socket #udp可以跟任意人联接. sk = socket.socket(type = socket.SOCK_DGRAM) #udp协议 实例化对象 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) #发送 #server不能接收Q退出..不然就不能跟别人联接了..只需要客户端退出就行了
#client 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)
8.黏包 struct 模块
同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。
import struct num1 = 129469649 num2 = 123 num3 = 8 ret1 = struct.pack('i',num1) #把一个整数pack print(len(ret1)) ret2 = struct.pack('i',num2) print(len(ret2)) ret3 = struct.pack('i',num3) print(len(ret3)) print(struct.unpack('i',ret1)) #转为原来数值129469649 print(struct.unpack('i', ret2)) print(struct.unpack('i', ret3))
8.1 tcp协议的现象
#server端 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' 补0.向左补4个0 # 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() # 粘包现象 # 只出现在tcp协议中,因为tcp协议 多条消息之间没有边界,并且还有一大堆优化算法 # 发送端 : 两条消息都很短,发送的间隔时间也非常短 # 接收端 : 多条消息由于没有及时接收,而在接收方的缓存短堆在一起导致的粘包 # 解决粘包问题的本质 :设置边界 # server端 # 1.先计算要发送的数据的长度 # 通过struct模块把长度转换成固定的4字节 # 发送4个字节的长度 # 发送内容....
#client端 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()
9.基于udp 协议的多人聊天 自动识别用户,
server端 import socket friend ={'小明':34,'小王':32} sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9000)) while True: mag,addr = sk.recvfrom(1024) mag = (mag.decode('utf-8')) name,message = mag.split('|') print('\033[1;%sm;%s:%s\033[0m'%(friend.get(name,30),name,message)) mag = input('<<<') sk.sendto(mag.encode('utf-8'),addr)
client端 import socket name = '小明' while True: sk = socket.socket(type=socket.SOCK_DGRAM) server = (('127.0.0.1',9000)) mag = input('<<<') if mag.upper() =='Q':break mag = '%s|%s'%(name,mag) sk.sendto(mag.encode('utf-8'),server) mag = sk.recv(1024).decode('utf-8') if mag.upper() == 'Q': break print(mag)
10.用tcp实现接收发送小文件和大文件的功能
servet端 #接收 import socket import json import struct sk = socket.socket() # sk.bind(('127.0.0.1'),9000) sk.bind(('127.0.0.1',7003)) sk.listen() conn,addr = sk.accept() #为了解决粘包现象,先接收4个字节. # mag = conn.recv(1024).decode('utf-8') mag = conn.recv(4) dic_len = struct.unpack('i',mag)[0] dic_len = conn.recv(dic_len).decode('utf-8') mag = json.loads(dic_len) with open(mag['filename'],mode='wb') as f: while mag['filesize'] > 0: content = conn.recv(1024) #先接1024字节 mag['filesize'] -= len(content) #在接剩余的 f.write(content) conn.close() sk.close()
client端 mport socket import json import os import struct sk = socket.socket() sk.connect(('127.0.0.1',7003)) #文件/文件名 addr_path = r'E:\zl\day07 课上视频\day07 课上视频\04 python fullstack s22 day07 编码的进阶【it资源社区www.it0365.com】.mp4' filename = os.path.basename(addr_path) #文件 filesize = os.path.getsize(addr_path) #文件大小 dic = {'filename':filename,'filesize':filesize} new_mag =json.dumps(dic) #json传字符串 new_mag = new_mag.encode('utf-8') mlab = struct.pack('i',len(new_mag)) sk.send(mlab) ## 4个字节 表示字典转成字节之后的长度 sk.send(new_mag) ## 具体的字典数据 #如要传大文件的话.需要减去大小 #还有为了防止粘包.. with open(addr_path,mode='rb') as f: while filesize>0: content = f.read(1024) #先读1024个字节 filesize -= len(content) #总字节减去传送过去的 sk.send(content) sk.close()
11.检验客户端的合法性
server端 import os import socket import hashlib secket_key = b'zeng' sk = socket.socket() # sk.bind(('127.0.0.1',9001)) sk.bind(('127.0.0.1',7005)) sk.listen() conn,addr = sk.accept() # 创建一个随机的字符串 ret = os.urandom(32) # 发送随机字符串 conn.send(ret) # 根据发送的字符串 + secrete key 进行摘要 sha = hashlib.sha1(secket_key) sha.update(ret) res = sha.hexdigest() #获取密文 # 等待接收客户端的摘要结果 res_new = conn.recv(1024).decode('utf-8') # 做比对 if res_new == res: print('合法的') # 如果一致,就显示是合法的客户端 # 并可以继续操作 conn.send(b'ok') else: conn.close() # 如果不一致,应立即关闭连接
client端 import socket import hashlib secket_key = b'zeng' sk = socket.socket() # sk.connect(('127.0.0.1',9001)) sk.connect(('127.0.0.1',7001)) # 接收客户端发送的随机字符串 ret = sk.recv(32) # 根据发送的字符串 + secret key 进行摘要 sha=hashlib.sha1(secket_key) #密钥 sha.update(ret) #发送 res = sha.hexdigest() #获取密文 # 摘要结果发送回server端 sk.send(res.encode('utf-8')) # 继续和server端进行通信 mag = sk.recv(1024) print(mag)
12.socketserver ,后续所有的server端.都用这模块.需要记住这代码.client端不变
以后用tcp时.需要建立目录.目录为socketserver .在此目录下,还需要建立server和client
#server import socketserver import time 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(1) except ConnectionRefusedError: break server = socketserver.ThreadingTCPServer(('127.0.0.1',7004),Myserver) server.serve_forever()
#client import socket sk=socket.socket() sk.connect(('127.0.0.1',7004)) while True: sk.send(b'hehe') mag = sk.recv(1024).decode('utf-8') print(con,mag)