• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
KK筑梦人
博客园    首页    新随笔    联系   管理    订阅  订阅

上节复习、验证客户端连接的合法性、hmac模块、socketserver模块的引入 第二十八天 2018.11.17

上节复习:

解决黏包问题:(面试题)

  为什么会出现黏包现象?

    首先只有在TCP协议中才会出现黏包现象

    是因为TCP协议是面向流的协议

    在发送的数据传输的过程中还有缓存机制来避免数据丢失

    因此,在连续发送小数据的时候,以及接收大小不符的时候的都容易出现黏包现象

    本质还是因为我们在接收数据的时候不知道发送的数据的长短

解决黏包问题

  在传输大量数据之前先告诉接收端要发送的数据大小

  如果想更漂亮的解决问题,可以通过struct模块来定制协议

面试题:

一台服务器如何在网络中找到另一台服务器
osi五层模型
    应用层
    传输层      tcp协议和udp协议
    网络层      ip协议(ipv4 ipv6)    路由器
    数据链路层  arp协议(利用ip找mac) 交换机
    物理层
tcp协议  可靠地 面向连接的 字节流传输
udp协议  不可靠的 无连接的 高效的传输
TCP协议中 三次握手和四次挥手
粘包 针对 tcp协议
    拆包机制 nagel算法(合包) 缓存机制
    面向流的传输 - 数据与数据之间没有边界
    粘包机制可能发生在发送端和接收端
udp协议不会粘包
    面向数据包的传输方式
    不可靠
对于空消息:
    tcp协议不能发空消息
    udp协议可以

struct模块

  pack、unpack

  模式:'i'

  pack之后的长度:4个字节

  unpack后拿到的数据是一个元组:元组的第一个元素才是pack的值

验证客户端连接的合法性:

访问网站-------->client---->server      ip_port      server

检测一下客户端是否合法      不依靠登录认证

hmac模块:

import hmac  模块hashlib
h = hmac.new()        #secret_key   密钥,你想进行加密的bytes
密文的内容 = h.digest()        #  
hmac.compare_digest()         #对比密文与另外一个密文
密钥---->执行时不可改变、可配置的

验证客户端的合法性:

server端(服务端):

#服务端:利用os生成一个随机32位字节发给客户端
#服务端中OS生成的字节和密钥组合利用hmac进行摘要与客户端发来的摘要相比较
#即:利用服务端和客户端使用相同的摘要,在密钥相同的情况下---->判定为合法
import os                      #使用os的随机方法,生成一次每次都不一样的字节和原密钥组合生成新密钥
import socket
import hmac                    #用来加密---->密钥和OS生成的组合
def check_conn(conn):          #面向函数---->用函数
    msg = os.urandom(32)       #每次执行都随机生成一个32位的字节
    conn.send(msg)
    h= hmac.new(secret_key,msg)            #用hamc加密密钥和msg随机生成的一个32位字节
    digest = h.digest()                    #bytes类型---->取摘要后的密文内容
    if conn.recv(1024) == digest:          #如果server生成的和client生成的一致即为合法
        print( '合法的客户端')
        return  True
    else:
        print('非法的客户端')
        return  False

secret_key = b'egg'  # 密钥
sk = socket.socket()
sk.bind(('127.0.0.1',8090))           #ip_port
sk.listen()                            #建立监听

conn,addr = sk.accept()                #建立conn连接
ret = check_conn(conn)
while ret:                            #检验合法性---->合法后进入无限循环聊天
    info = input('>>>')
    conn.send(info.encode('utf-8'))          #注意是conn.send---->conn是连接
    talk = conn.recv(1024)
    print(talk.decode('utf-8'))
conn.close()                          #结束聊天后才关闭掉,加个判断break即可
sk.close()

client端(客户端):

#客户端
import socket
import hmac
sk = socket.socket()
sk.connect(('127.0.0.1',8090))

msg = sk.recv(1024)          #接收服务端中os模块随机生成的一个32位字节
# 用和server端相同的方法对这个字符串进行摘要
secret_key = b'egg'  # 密钥
h = hmac.new(secret_key,msg) #用hmac进行摘要
ret = h.digest()             #取摘要后的密文内容
sk.send(ret)
talk = sk.recv(1024)
if talk:                 #server端已经验证过客户端的合法性了
    print(talk.decode('utf-8'))
    while True:
        info = input('>>>')
        sk.send(info.encode('utf-8'))
        talk = sk.recv(1024)
        print(talk.decode('utf-8'))
sk.close()

socketserv模块:

socket tcp---->只能和一个client通信

socketserver tcp---->可以和多个通信

import socketserver(类似并发)

server端整个过程相当于面向对象中定义一个类,然后将该类实例化为一个对象,并给这个实例化的对象一些操作

定义的类必须继承socketserver模块中的BaseRequestHandler类,定义的类无init方法(出现的init创建self对象向该类父类中找),必

须有handle方法

实例化为:对象名 = socketserver.ThreadingTCPServer((ip,端口号),类名)

并给该对象一个永远开启的服务(被多个用户所使用的,因此需要永久开启)

server端:

# TCP服务端一对多服务---->socketserver模块
# 每次运行后请关闭显示器窗口---->因为永久运行,否则就会报错端口和套接字被占用
import socketserver

class MyServer(socketserver.BaseRequestHandler):     #必须继承socketserver模块中的BaseRequestHandler类
    #Base---->父类,handler---->处理   必须继承此类
    def handle(self):              #必须有这个方法
        while True:
            ret = self.request.recv(1024).decode('utf-8')       #self.request相当于一个conn
            print(ret)
            if ret == 'bye':
                self.request.send('bye'.encode('utf-8'))
                self.request.close()
                break        #因为客户端永久运行,即使bye掉本次,还可以输入
            info = input('>>>').encode('utf-8')
            self.request.send(info)


server = socketserver.ThreadingTCPServer(('127.0.0.1',9090),MyServer)      #类的实例化
#Thread---->线程     一个程序里运行起来只有一个线程---->每一个线程都可以请求cup
server.serve_forever()            #永远开启一个服务
#因为对多个人提供连接服务,因此不会关闭---->类似于加个永久运行

client端:  

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9090))
while True:
    msg = input('>>>')
    if msg == 'bye':
        sk.send('bye'.encode('utf-8'))
        break
    sk.send(('花花:'+msg).encode('utf-8'))
    talk = sk.recv(1024)
    print(talk.decode('utf-8'))
sk.close()

  

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9090))
while True:
    msg = input('>>>')
    if msg == 'bye':
        sk.send('bye'.encode('utf-8'))
        break
    sk.send(('明明:'+msg).encode('utf-8'))
    talk = sk.recv(1024)
    print(talk.decode('utf-8'))
sk.close()

看socketserver源码:

bind、listen

conn、addr=accept

self.request = conn

看源码:          第一整理多个类之间的继承关系

      每一个类中有哪些方法,要大致列出来

      所有的self对象的调用要清楚的了解 到底是谁的对象

      所有的方法调用要退回到最子类的类中开始寻找,逐级向上

  

posted @ 2018-11-17 16:39  KK筑梦人  阅读(101)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3