网络编程 socket常用方法 hmac模块 socketserver模块

socket常用方法:
服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv()            接收TCP数据   s 相当于 conn
s.send()            发送TCP数据    #(拆包在发送)   常用
s.sendall()         发送TCP数据   $(一次性发送)
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址  s = conn
s.getsockname()     当前套接字的地址         s = conn
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字
l例 #print(conn.getsockname())
面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout(5)      设置阻塞套接字操作的超时时间  #参数5指等待阻塞5秒,超过5秒报错
s.gettimeout()      得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件
send 和 sendall 的区别:
对于程序员来说,用起来是没有什么区别的
实际上,在socket底层对于两个方法的封装有却别:
 send(num) 此方法会尝试先发送n个数据(n<num),接下来再尝试发送num-n
sendall(num)  此方法会尝试一次性将num个数据发送出去
(重点):
setblocking(True)  阻塞
setblocking(False) 非阻塞
settimeout(int)    针对阻塞状态,设置一个延时等待
gettimeout()       获得延时的时间

服务器可以开多个端口,可以设置阻塞时间:

import socket
sk = socket.socket()
sk.setblocking(True)  # 设置 阻塞非阻塞
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 允许一个端口号重用的解决方法
sk.bind(('127.0.0.1',9090))
sk.listen()
while 1:
    try:
        conn,addr = sk.accept()
        print(123)
        conn.recv(1024)
        print(123)
        # while 1:
        #     try:
        #         msg = conn.recv(1024)
        #         print(msg)
        #         break
        #     except BlockingIOError:
        #         pass
    except BlockingIOError:
        print(789)

# print(conn.getpeername())
# print(sk.getsockname())
# print(sk.getsockopt())


conn.close()
sk.close()
服务器:
import socket

sk = socket.socket()
sk.setblocking(True) # 设置 阻塞非阻塞
sk.settimeout(5)   #阻塞5秒,超过5秒接收不到会报错
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 允许一个端口号重用的解决方法
sk.bind(('127.0.0.1',9090))
sk.listen()
print(123)
print(sk.gettimeout())  # 获取阻塞时间
conn,addr = sk.accept()
print(456)
conn.close()
sk.close()
服务器
客户端:
import socket
import time
sk = socket.socket()
sk.connect_ex(('127.0.0.1',9090))

time.sleep(2)
sk.send(b'qweqweqweqweqweqweqw')
sk.close()
客户端
设置服务器客户端不用写encode,decode方法(调用类)
import socket

class MySocket(socket.socket):
    def __init__(self,encoding = 'utf-8'):
        self.encoding = encoding
        super(MySocket, self).__init__(type=socket.SOCK_DGRAM)

    def my_recv(self,num):
        msg,addr = self.recvfrom(num)
        return msg.decode(self.encoding),addr

    def my_send(self,msg,addr):
        return self.sendto(msg.encode(self.encoding),addr)
View Code

验证客户连接:

方法1:固态加盐:

服务器:
import socket
import hashlib
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
conn,addr = sk.accept()
key = '天王盖地虎'      # 这个是固定盐
ch = '这是一个随机字符串'
conn.send(ch.encode('utf-8'))   # 把随机字符串发给client
md5_obj = hashlib.md5(key.encode('utf-8'))
md5_obj.update(ch.encode('utf-8'))
re = md5_obj.hexdigest()
# 固定的用盐的加密方式
client_re = conn.recv(1024).decode('utf-8')   # 接收client端加密后的结果

if re == client_re:
    print('你好机油!')
    '''收发数据的逻辑'''
else:
    print('你根本不是老司机')
    conn.close()
sk.close()
服务器
客户端:
import socket
import hashlib
sk = socket.socket()
sk.connect(('127.0.0.1',9090))

key = '天王盖地虎'
ch = sk.recv(1024)
md5_obj = hashlib.md5(key.encode('utf-8'))
md5_obj.update(ch)
re = md5_obj.hexdigest()
sk.send(re.encode('utf-8'))
sk.close()
客户端

方法2;用真实的随机字符串

import socket
import hashlib
import os
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
conn,addr = sk.accept()
key = '天王盖地虎'  # 这个是固定盐
ch = os.urandom(10)
conn.send(ch)  # 把随机字符串发给client
md5_obj = hashlib.md5(key.encode('utf-8'))
md5_obj.update(ch)
re = md5_obj.hexdigest()
# 固定的用盐的加密方式
client_re = conn.recv(1024).decode('utf-8')  # 接收client端加密后的结果

if re == client_re:
    print('你好机油!')
    '''收发数据的逻辑'''
else:
    print('你根本不是老司机')
    conn.close()
sk.close()
服务器
客户端:
import socket
import hashlib
sk = socket.socket()
sk.connect(('127.0.0.1',9090))

key = '天王盖地虎'
ch = sk.recv(1024)
md5_obj = hashlib.md5(key.encode('utf-8'))
md5_obj.update(ch)
re = md5_obj.hexdigest()
sk.send(re.encode('utf-8'))

sk.close()
客户端

方法3:用hmac模块实现机密:

服务器:
import socket
import hashlib
import os
import hmac
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
conn,addr = sk.accept()
key = '天王盖地虎'  # 这个是固定盐
ch = os.urandom(10)
conn.send(ch)   # 把随机字符串发给client
obj = hmac.new(key.encode('utf-8'),ch)
re = obj.digest()
# 固定的用盐的加密方式
client_re = conn.recv(1024)  # 接收client端加密后的结果

if re == client_re:
    print('你好机油!')
    '''收发数据的逻辑'''
else:
    print('你根本不是老司机')
    conn.close()
sk.close()
服务器
客户端:
import socket
import hashlib
import hmac
sk = socket.socket()
sk.connect(('127.0.0.1',9090))

key = '天王盖地虎'
ch = sk.recv(1024)
obj = hmac.new(key.encode('utf-8'),ch)
re = obj.digest()
sk.send(re)

sk.close()
客户端
hmac模块(算法模块)
定义HMAC需要一个加密用散列函数(表示为H,可以是MD5或者SHA-1)和一个密钥K。我们用B来表示数据块的字节数。(以上所提到的散列函数的分割数据块字长B=64),用L来表示散列函数的输出数据字节数(MD5中L=16,SHA-1中L=20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比B大,则首先用使用散列函数H作用于它,然后用H输出的L长度字符串作为在HMAC中实际使用的密钥。一般情况下,推荐的最小密钥K长度是L个字节。
使用hmac和普通hash算法非常类似。hmac输出的长度和原始哈希算法的长度一致。需要注意传入的key和message都是bytes类型,str类型需要首先编码为bytes。
HMAC的应用
hmac主要应用在身份验证中,它的使用方法是这样的:
(1) 客户端发出登录请求(假设是浏览器的GET请求)
(2) 服务器返回一个随机值,并在会话中记录这个随机值
(3) 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
(4) 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法
在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的hmac结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使hmac只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了hmac算法,比如php的mhash、python的hmac.py、java的MessageDigest类,在web验证中使用hmac也是可行的,用js进行md5运算的速度也是比较快的。
socketserver模块     (解决tcp)
 现在没有学习并发编程,现在解决不了tcp协议中一个服务器同时连接多个客户端
socketserver,看其名字,就知道是一个socket的服务器模块的使用,在这个模块中,主要也就是实现服务器类的相关功能,在其中,也就是将socket模块和select模块进行了封装,从而创建了一些基类供人使用。
SocketServer内部使用 IO多路复用以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
原理:
1、服务器启动socket监听端口
2、服务器内部利用while循环监视句柄的变化
3、客户端请求
4、服务器为这个请求分配线程或进程(底层调用select)。
SocketServer模块有两个方法ThreadingTCPServer和ForkingTCPServer,分别创建线程或者进程
服务器:
import socketserver
#sk  conn
import json
import hashlib
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        while 1:
            dic_str = self.request.recv(1024).decode('utf-8') # 接收到序列化的字典
            dic = json.loads(dic_str) # 反序列化字典,字典中有用户名和密码
            # 用户名当盐   加上密码去做md5
            with open('info',encoding='utf-8') as f:
                for user_info in f:# 旭哥 | 7d79a61dd0bd94a3df2f765ac12fe492
                    username,pawd = user_info.split('|')
                    if username.strip() == dic['username']:  # 先去对比用户名正确与否
                        '''如果用户名存在的情况下
                         加密方式 : 用户名当盐,对密码加密'''
                        md5_obj = hashlib.md5(dic['username'].encode('utf-8'))
                        md5_obj.update(dic['passwd'].encode('utf-8'))
                        re = md5_obj.hexdigest()
                        if re == pawd.strip(): # 拿加密完的密码密文对比文件中密码的密文
                            self.request.send(b'success!')
                            '''通信的逻辑'''
                        else:
                            '''失败,返回给客户端信息.....'''
                            self.request.send(b'failed!')
                        break
                else:
                    '''对应for   如果用户名不存在'''
                    print('用户不存在!')

server = socketserver.TCPServer(('127.0.0.1',9090),Myserver)  # 绑定一个服务
server.serve_forever()   # 永久性开启服务
服务器
客户端:
import socket
import time
import json
sk = socket.socket()
sk.connect(('127.0.0.1',9090))
dic = {'username':None,'passwd':None}
while 1:
    username = input('用户名>>>')
    passwd = input('密码>>>')
    dic['username'] = username
    dic['passwd'] = passwd
    dic_str = json.dumps(dic)
    sk.send(dic_str.encode('utf-8'))
    print(sk.recv(1024))
sk.close()
客户端

相关推荐