网络知识基础介绍:
网络的开发架构2种:
C/S:客户端/服务端 的架构
B/S:基于web可以浏览到的 ,统一入口
参考博客:
www.cnblogs.com/Eva-j/articles/8066842.html
socket模块,基于tcp的通信:
是应用层与tcp/ip协议中间软件的抽象层,它是一组接口,socket其实就是一个门面模式他把负载的tcp/ip协议隐藏在socket接口后面,让socket去组织数据
站在你的角度 socket就是一个模块,我们通过调用模块实现的方法建立两个进程之间的链接和通信
参考博客:
www.cnblogs.com/Eva-j/articles/8244551.html
基于socker模块的代码,需要有server ,client 两个代码块 ,
信息交互中必须对应着一发一收 , 一收一发
# server import socket sk = socket.socket() #sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDE,1) # 允许这个链接被重用,否则避免服务重启时会报,这个链接已占用错误 #服务端绑定一个地址和端口 sk.bind(('127.0.0.1',8080)) sk.listen() # 监听 客户端对这个地址的请求 conn,addr = sk.accept() # 服务端先接受客户端请求,几下客户端的地址 ,和一个会话 ret = conn.recv(1024) # 服务器接受客户发来的信息 print(ret) conn.send(b'hello') # 服务器发送一个消息 ret1 = conn.recv(1024) print(ret1.decode('utf-8')) # 接受中文 conn.close() # 关闭会话链接 sk.close() # 关闭socket
# 客户端 import socket ck = socket.socket() ck.connect(('127.0.0.1',8080)) # 创建个链接 这里写服务器的地址 ck.send(b'hello') ret = ck.recv(1024) print(ret) ck.send(bytes('中午吃什么',encoding='utf-8')) ck.close()


# 服务端 # server import socket sk = socket.socket() #服务端绑定一个地址和端口 sk.bind(('127.0.0.1',8081)) sk.listen() # 监听 客户端对这个地址的请求 conn,addr = sk.accept() # 服务端先接受客户端请求,几下客户端的地址 ,和一个会话 while True: ret = conn.recv(1024).decode('utf-8') # 服务器接受客户发来的信息 print(ret) if ret == 'bye': conn.send(b'bye') break ret = input('>>>>>') conn.send(bytes(ret,encoding='utf-8')) # 服务器发送一个消息 conn.close() # 关闭会话链接 sk.close() # 关闭socket #客户端 # 客户端 import socket ck = socket.socket() ck.connect(('127.0.0.1',8081)) # 创建个链接 这里写服务器的地址 while True: ret = input(">>>>>>") ck.send(bytes(ret,encoding='utf-8')) ret1 = ck.recv(1024).decode('utf-8') print(ret1) if ret1 == 'bye': ck.send(b'bye') break ck.close()
基于UDP 编码
udp 是短连接,所以可以同时与多个进行会话,不会有阻塞现象
UDP 的server 不需要进行监听 ,也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来
客户端发送消息的同时会自带地址
消息回复时不仅需要发送消息,还需要把对方的地址带上,才知道发送给那个客户端
#server import socket sk = socket.socket(type=socket.SOCK_DGRAM) #udp sk.bind((127.0.0.1,8080)) miss,adrr = sk.recvfrom(1024) #接受客户的信息 print(miss.decode('utf-8') ) #打印消息 sk.sendto(b'bye',addr) # 服务器给客户端发必须带这客户端地址 sk.coled()
客户端 impoert socket ck = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) ck.sendto(b'hello',ip_port) ret,addr = ck.recvfrom(1024) 每次接受消息都会带着地址发过来 print(ret) ck.close()
执行命令模块subprocess
注意:这个模块是gbk编码的
import subprocess dis = input('>>>>') #设定一个命令的项,包含正确输出和错误输出 res = subprocess.Popen(dis,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #输出 print(res.stdout.read().decode('gbk')) #输出是bytes类型,需要转为gbk print(res.stderr.read().decode('gbk'))
python中,我们使用decode()和encode()来进行解码和编码
在python中,使用unicode类型作为编码的基础类型。即
decode encode
str ---------> unicode --------->str
好消息来了,对,那就是python3,在新版本的python3中,取消了unicode类型,代替它的是使用unicode字符的字符串类型(str),字符串类型(str)成为基础类型如下所示,而编码后的变为了字节类型(bytes)但是两个函数的使用方法不变:
decode encode
bytes ------> str(unicode)------>bytes
---------------------
基于tcp实现远程执行命令 ,在server端下发命令 ,在客户端执行,并将结果返回服务端
#服务器端发送命令 import socket #建立tcp链接 sk = socket.socket() sk.bind(('127.0.0.1',8080)) #绑定ip与端口 sk.listen() conn,addr = sk.accept() #建立链接 while True: dis = input('>>>>>>>>>>>') if dis == 'q': conn.send(b'q') break conn.send(dis.encode('gbk')) ret = conn.recv(1024).decode('gbk') print(ret) conn.close() sk.close()
#客户端执行命令 import socket import subprocess ck = socket.socket() ck.connect(('127.0.0.1',8080)) while True: dis = ck.recv(1024).decode('gbk') if dis == 'q': break ret = subprocess.Popen(dis,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) ck.send(ret.stdout.read()) ck.send(ret.stderr.read()) ck.close()
黏包现象
在上面执行的代码中就出现了黏包现象
黏包现象是指:基于tcp的链接,udp是不会产生黏包的,当发送的数据包多大,会就数据包切分发送,将未发送的部分放在内核空间中,当下次客户端在请求时,将剩下部分发送过去,这样就会造成黏包,数据混乱
而udp传输时会将未发送的部分数据包直接丢弃,所以不会造成黏包,但会有丢包现象
基于udp的远程命令传输:
#服务器端 import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8080)) mis,addr = sk.recvfrom(1024) while True: dis = input('>>>>') if dis == 'q': sk.sendto(dis.encode('gbk'),addr) break sk.sendto(dis.encode('gbk'),addr) ret = sk.recvfrom(4096)[0].decode('gbk') print(ret) sk.close()
客户端 import socket import subprocess ck = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) ck.sendto(b'hello',ip_port) while True: dis = ck.recvfrom(1024)[0].decode('gbk') if dis == 'q': break ret = subprocess.Popen(dis,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) ck.sendto(ret.stdout.read(),ip_port) ck.sendto(ret.stderr.read(),ip_port) #print((ret.stderr.read(),ip_port)) ck.close()
黏包的触发
有两种现象:
1 连续收包
import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() ret1 = conn.recv(2) ret2 = conn.recv(10) #在服务端有两次接受 print(ret1) print(ret2) sk.close() import socket ck = socket.socket() ck.connect(('127.0.0.1',8080)) ck.send(b'hello you') ck.close() #连续的小数据包会被合并
2 连续的发送小包
import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() ret1 = conn.recv(12) print(ret1) print(ret2) sk.close() import socket ck = socket.socket() ck.connect(('127.0.0.1',8080)) ck.send(b'xiao') ck.send(b'hello you') ck.close()
解决黏包方法2个:
确定我到底要接收多大的数据


import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() while True: cmd = input('>>>>') if cmd == 'q': conn.send(b'q') break conn.send(cmd.encode('gbk')) num = conn.recv(1024).decode('utf-8') conn.send(b'ok') ret = conn.recv(int(num)).decode('gbk') print(ret) conn.close() sk.close()


import socket import subprocess sk = socket.socket() sk.connect(('127.0.0.1',8080)) while True: dis = sk.recv(1024).decode('gbk') if dis == 'q': break res = subprocess.Popen(dis,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() num = str(len(std_out) + len(std_err)).encode('utf-8') sk.send(num) sk.recv(1024) sk.send(std_out) sk.send(std_err) sk.close()
方法2 用struct模块
该模块可以把数据类型,转为固定长度的bytes
import struct ret = struct.pack('i',4096) # i 代表int ,就是即将要把数字转为固定长度的bytes,4个字节 print(ret) num = struct.unpack('i',ret) #拿到一个元祖 print(num[0]) # 拿到这个数
只有pack 与unpack 两个方法
pack 将int 转为4位二进制
unpack 将4为二进制转为 int


import socket import struct sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() while True: cmd = input('>>>>') if cmd == 'q': conn.send(b'q') break conn.send(cmd.encode('gbk')) num = conn.recv(4) num = struct.unpack('i',num)[0] ret = conn.recv(int(num)).decode('gbk') print(ret) conn.close() sk.close()


import socket import subprocess import struct sk = socket.socket() sk.connect(('127.0.0.1',8080)) while True: dis = sk.recv(1024).decode('gbk') if dis == 'q': break res = subprocess.Popen(dis,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() len_num = len(std_out) + len(std_err) num = struct.pack('i',len_num) sk.send(num) sk.send(std_out) sk.send(std_err) sk.close()
博客参考:
www.cnblogs.com/Eva-J/articles/8244551.html#_label4
自己定制报头:
在一些复杂的应用上会用到
如传输文件上,带上文件的属性,一起发给对方
head = {'filename':'test','filesize':409600,'filetype':'txt','filepath':r'\usr\bin'}
#1先发送报头的长度
2 send(head) #发送时先发送我的报头
3 从报头中获取filesize
4 根据size 去接受文件
验证客户端的合法性
hmac模块
使用:
h = hmac.new() #你想进行加密的bytes 密文 = h.digest() hmac.compare_digest() #对比 密文对比


import socket import os import hmac key = b'egg' sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() def check_conn(conn): msg = os.urandom(32) #随机生成一个32为二进制 conn.send(msg) h = hmac.new(key,msg) #对key进行加密 dis = h.digest() # 生成一个密文 client_dis = conn.recv(1024) #接受客户端发来的密文 return hmac.compare_digest(dis,client_dis) #比较这两个密文一样返回TRue conn,addr = sk.accept() res = check_conn(conn) if res: print("客户端合法") conn.close() else: print('客户端不合法') conn.close() sk.close()


import socket import hmac key = b'egg' ck = socket.socket() ck.connect(('127.0.0.1',8080)) msg = ck.recv(1024) # 服务端传来的那个32位bytes h = hmac.new(key,msg) dis = h.digest() ck.send(dis) ck.close()
socketserver模块 他可以实时与多个客户端通信,是在socket 外进行了一层封装
看源码:
1 多个类之间的继承关系先整理
2 每个类中的有哪些方法
3 所有的self的对象调用要清楚了解到底是谁的对象
4 所有的方法调用哪个要退回最子类的类中开始逐级寻找


import socketserver #需要先创建个类 class myserver(socketserver.BaseRequestHandler): # 类必须继承这个父类 def handle(self): #必须有这个方法 while True: msg = self.request.recv(1024).decode('utf-8') #self.request 就相当于conn.recv() if msg == 'q': break print(msg) miss = input('>>>>') self.request.send(miss.encode('utf-8')) #这个模块引入了线程的概念,来实现并发 if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),myserver) server.serve_forever() # 永远启动这个线程


import socket sk = socket.socket() sk.connect(('127.0.0.1',8080)) while True: msg = input('>>>>') if msg == 'q': break sk.send(msg.encode('utf-8')) ret = sk.recv(1024).decode('utf-8') print(ret) sk.close()


import json import hashlib import socketserver def md5_pwd(user,pwd): md5_obj = hashlib.md5(user.encode('utf-8')) md5_obj.update(pwd.encode('utf-8')) ret = md5_obj.hexdigest() return ret def login(userinfo): user_dic = json.loads(userinfo) passwd = md5_pwd(user_dic['username'], user_dic['passwd']) with open('userinfo') as f: for line in f: user, pwd = line.split('|') if user_dic['username'] == user and passwd == pwd: print('登录成功') break class MyServer(socketserver.BaseRequestHandler): def handle(self): userinfo = self.request.recv(1024).decode('utf-8') login(userinfo) server = socketserver.ThreadingTCPServer( ('127.0.0.1',9000), MyServer) server.serve_forever()


import json import socket ADDR = ('127.0.0.1',9000) def get_socket(): sk = socket.socket() sk.connect(ADDR) return sk # 输入账号 username = input('username >>>') passwd = input('password >>>') if username.strip() and passwd.strip(): sk = get_socket() dic = {'username':username,'passwd':passwd} str_dic = json.dumps(dic) sk.send(str_dic.encode('utf-8')) sk.close() # 连接socket
ftp 作业分析
www.cnblogs.com/Eva-J/articles/7642557.html