python 网络编程 socket模块中的常用方法 验证客户端链接的合法性

今日内容:

1介绍socket模块中一些其他常用方法

  send和sendall 的区别

  对于程序员来说,用起来是没有什么区别的

     实际上,在socket底层对于两个方法的封装有区别:
      send(num)此方法会尝试先发送n个数据(n<num),接下来再尝试发送num-n

      sendall(num)此方法会尝试一次性将num个数据发送出去

(重点) setbiocking(Ture) 阻塞

    setbiocking(False)非阻塞

    settimeout(int) 针对阻塞状态,设置一个延时等待

    gettimeout()  获得延时的时间

2  关于客户端验证的事情

    用到一个新的小模块 hmac(内置) MD5

# 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
# 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()



# ===============================================
# 调用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()



server
# 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()

# =============================================
# 此代码用hmac模块实现机密
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()


client

  

  

3socketserver

    现在没有学习并发编程,现在解决不了tcp协议中服务器同时连接多个客户端

# import socketserver
# #sk  conn 等效于 self.requset.
# class Myserver(socketserver.BaseRequestHandler):
#     def handle(self):
#         # print(123)
#         # self.request.send()
#         print(self.request.recv(1024).decode('utf-8'))
#
# server = socketserver.TCPServer(('192.168.19.200',9090),Myserver)
#
# server.serve_forever()

# ========================================
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()# 永久性开启服务


server

  

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()


client

  

.socket的更多方法介绍

服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据
s.sendall()         发送TCP数据
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字

面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操作的超时时间
s.gettimeout()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件

更多方法

更多方法

 

官方文档对socket模块下的socket.send()和socket.sendall()解释如下:

socket.send(string[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data.

send()的返回值是发送的字节数量,这个数量值可能小于要发送的string的字节数,也就是说可能无法发送string中所有的数据。如果有错误则会抛出异常。

–

socket.sendall(string[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from string until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

尝试发送string的所有数据,成功则返回None,失败则抛出异常。

故,下面两段代码是等价的:

#sock.sendall('Hello world
')

#buffer = 'Hello world
'
#while buffer:
#    bytes = sock.send(buffer)
#    buffer = buffer[bytes:]

send和sendall方法

send和sendall方法

  

如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现

 

#_*_coding:utf-8_*_
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    认证客户端链接
    :param conn:
    :return:
    '''
    print('开始验证新链接的合法性')
    msg=os.urandom(32)
    conn.sendall(msg)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    respone=conn.recv(len(digest))
    return hmac.compare_digest(respone,digest)

def data_handler(conn,bufsize=1024):
    if not conn_auth(conn):
        print('该链接不合法,关闭')
        conn.close()
        return
    print('链接合法,开始通信')
    while True:
        data=conn.recv(bufsize)
        if not data:break
        conn.sendall(data.upper())

def server_handler(ip_port,bufsize,backlog=5):
    '''
    只处理链接
    :param ip_port:
    :return:
    '''
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(backlog)
    while True:
        conn,addr=tcp_socket_server.accept()
        print('新连接[%s:%s]' %(addr[0],addr[1]))
        data_handler(conn,bufsize)

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    server_handler(ip_port,bufsize)

服务端

服务端

  

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    验证客户端到服务器的链接
    :param conn:
    :return:
    '''
    msg=conn.recv(32)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    conn_auth(tcp_socket_client)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

客户端(合法)

客户端(合法)

  

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

客户端(非法:不知道加密方式)

客户端(非法:不知道加密方式)

  

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang1111'
def conn_auth(conn):
    '''
    验证客户端到服务器的链接
    :param conn:
    :return:
    '''
    msg=conn.recv(32)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    conn_auth(tcp_socket_client)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

客户端(非法:不知道secret_key)

客户端(非法:不知道secret_key)

socketserver

import socketserver
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999

    # 设置allow_reuse_address允许服务器重用地址
    socketserver.TCPServer.allow_reuse_address = True
    # 创建一个server, 将服务地址绑定到127.0.0.1:9999
    server = socketserver.TCPServer((HOST, PORT),Myserver)
    # 让server永远运行下去,除非强制停止程序
    server.serve_forever()

server端

server端
import socket

HOST, PORT = "127.0.0.1", 9999
data = "hello"

# 创建一个socket链接,SOCK_STREAM代表使用TCP协议
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))          # 链接到客户端
    sock.sendall(bytes(data + "
", "utf-8")) # 向服务端发送数据
    received = str(sock.recv(1024), "utf-8")# 从服务端接收数据

print("Sent:     {}".format(data))
print("Received: {}".format(received))

client

client