socketserver和socket的补充(验证客户端合法性)
生活随笔
收集整理的這篇文章主要介紹了
socketserver和socket的补充(验证客户端合法性)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、socket的補充
1、參數
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
參數說明:
2、socket更多方法介紹
官方文檔對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\n')buffer = 'Hello world\n'
while buffer:bytes = sock.send(buffer)buffer = buffer[bytes:] send和sendall
代碼 服務端: import os import hmac import socketdef auth(conn):msg = os.urandom(32) # 生成一個32位的隨機的字節碼(urandom生成的字節碼就是bytes類型的)conn.send(msg) # 把這個隨機的字節碼發送到client端# hmac接收兩個參數,第一個參數相當于hashlib的鹽,第二個參數是我們隨機生成的字節碼,兩個參數都是bytes類型result = hmac.new(secret_key,msg) # 處理這個隨機字節碼,socket_key是鹽res = result.hexdigest() # 得到結果(字符串)client_digest = conn.recv(1024) # 接收client端處理的結果if res == client_digest.decode('utf-8'):print('合法的連接') # 對比成功可以繼續通信return Trueelse:print('不合法連接') # 不成功return Falsesecret_key = b'xiaoming' # hmac的鹽 sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() if auth(conn):msg = conn.recv(1024) # True正常的和client端進行溝通print(msg.decode('utf-8'))conn.close() else:conn.close() # False 直接關閉和這個客戶端的連接 sk.close()客戶端: import hmac import socketdef auth(sk):msg = sk.recv(32) # 接收服務端傳來的隨機字節碼result = hmac.new(secret_key,msg) # 處理接收到的隨機字節碼res = result.hexdigest() # 得到結果sk.send(res.encode('utf-8')) # 把結果發回給服務端,讓服務端進行驗證 secret_key = b'xiaoming' # 因為鹽是程序員自己設置的,那么程序員寫的客戶端肯定知道自己的鹽是什么 sk = socket.socket() sk.connect(('127.0.0.1',8080)) auth(sk) sk.send(b'connect success') # 進行其他正常的和server端的溝通 sk.close()二、socketserver 正常服務端的socket,每一次只能連接一個客戶端,只有跟當前客戶端斷開連接后才能和下一個客戶端連接, 而用socketserver可以跟多個客戶端同時連接(并發)。服務端: import socketserver # tcp協議的server端就不需要導入socket class Myserver(socketserver.BaseRequestHandler): # 繼承socketserver.BaseRequestHandler這個類def handle(self): # 必須繼承handle方法并重寫conn = self.request # self.request就是客戶端的對象while True: # 和客戶端進行交互conn.send(b'helloworld')print(conn.recv(1024).decode('utf-8'))# 設置allow_reuse_address允許服務器重用地址 socketserver.TCPServer.allow_reuse_address = True# 創建一個對象server,綁定ip和端口,相當于sk = socket.socket() sk.bind(('127.0.0.1',8888))這兩步的結合 server = socketserver.ThreadingTCPServer(('127.0.0.1',8888),Myserver)# 讓server一直運行下去,除非強制停止程序 server.serve_forever()客戶端: import socket sk = socket.socket() sk.connect(('127.0.0.1',8888)) while True:ret = sk.recv(1024)print(ret.decode('utf-8'))sk.send(b'hiworld') sk.close()解釋為什么一定要重寫handler方法: Myserver這個類沒有__init__方法,那么它就會去繼承使用父類BaseRequestHandler的__init__方法 看看BaseRequestHandler源碼: class BaseRequestHandler:def __init__(self, request, client_address, server):self.request = request # 獲取客戶端的連接(對象),設置為自己的屬性self.client_address = client_address # 客戶端的地址self.server = server self.setup()try:self.handle() # 初識化對象的時候執行handler方法finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):pass總結: 也就是說,子類繼承了父類的__init__方法,這個方法里面已經取到了客戶端的對象conn,和地址addr, 并且初始化的時候調用了handler方法,但是父類的handler方法并沒有實現任何功能,所以子類應該重寫handler方法便于與客戶端交互。實例:上傳文件server.pyimport json import struct import socketserver import operate_handlerclass MyFTP(socketserver.BaseRequestHandler):def handle(self):conn = self.requestlength = conn.recv(4)length = struct.unpack('i',length)[0]operate = (conn.recv(length)).decode('utf-8')operate_dic = json.loads(operate)opt = operate_dic['operate']usr = operate_dic['user']print(opt,usr)getattr(operate_handler,opt)(conn,usr)socketserver.TCPServer.allow_reuse_address = True server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyFTP) server.serve_forever()operate_handler.pyimport os import json import structbase_path = r'E:\PythonProject\ftp\server\root'def upload(conn,usr):fileinfo_len = conn.recv(4)fileinfo_len = struct.unpack('i',fileinfo_len)[0]fileinfo = (conn.recv(fileinfo_len)).decode('utf-8')fileinfo = json.loads(fileinfo)file_path = os.path.join(base_path,usr,fileinfo['filename'])file_path = os.path.abspath(file_path)with open(file_path,'wb') as f:while fileinfo['filesize']:content = conn.recv(20480)fileinfo['filesize'] -= len(content)f.write(content)print('接收完畢')client.pyimport os import json import struct import socket# 發送信息 def my_send(sk,operate_info):b_optinfo = (json.dumps(operate_info)).encode('utf-8')num = struct.pack('i',len(b_optinfo))sk.send(num)sk.send(b_optinfo)sk = socket.socket() sk.connect(('127.0.0.1',9000))# [登錄,注冊,退出]# 要進行的操作 operate_info = {'operate':'upload','user':'xiaoming'} my_send(sk,operate_info)# 選擇一個文發送到server端 file_path = r'F:\電影\電影\荒野生存.mp4'# 發送文件信息 file_name = os.path.basename(file_path) file_size = os.path.getsize(file_path) file_info = {'filename':file_name,'filesize':file_size} my_send(sk,file_info)# server端接收寫入 with open(file_path,'rb') as f:while file_size:content = f.read(20480)file_size -= len(content)sk.send(content) print('上傳完畢') sk.close()
| family | 地址系列應為AF_INET(默認值ipv4),AF_INET6(ipv6),AF_UNIX,AF_CAN或AF_RDS。 (AF_UNIX 域實際上是使用本地 socket 文件來通信) |
| type | 套接字類型應為SOCK_STREAM(默認值,tcp協議),SOCK_DGRAM(udp協議),SOCK_RAW或其他SOCK_常量之一。 SOCK_STREAM 是基于TCP的,有保障的(即能保證數據正確傳送到對方)面向連接的SOCKET,多用于資料傳送。 SOCK_DGRAM 是基于UDP的,無保障的面向消息的socket,多用于在網絡上發廣播信息。 |
| proto | 協議號通常為零,可以省略,或者在地址族為AF_CAN的情況下,協議應為CAN_RAW或CAN_BCM之一。 |
| fileno | 如果指定了fileno,則其他參數將被忽略,導致帶有指定文件描述符的套接字返回。 與socket.fromfd()不同,fileno將返回相同的套接字,而不是重復的。 這可能有助于使用socket.close()關閉一個獨立的插座。 |
2、socket更多方法介紹
| 服務端套接字函數 | |
| s.bind()? ? | 綁定(主機,端口號)到套接字 |
| s.listen() | 開始TCP監聽 |
| s.accept() | 被動接受TCP客戶的連接,(阻塞式)等待連接的到來 |
| 客戶端套接字函數 | |
| s.connect()? | 主動初始化TCP服務器連接 |
| s.connect_ex() | connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常 |
| 公共用途的套接字函數 | |
| s.recv()? | 接收TCP數據 |
| s.recvfrom() | 接收UDP數據 |
| ? | |
| s.send()? | 發送TCP數據 |
| s.sendall() | 發送TCP數據 |
| s.sendto()? | 發送UDP數據 |
| ? | |
| s.getpeername() | 連接到當前套接字的遠端的地址(client地址) |
| s.getsockname()? | 當前套接字的地址(server地址) |
| ? | |
| s.setsockopt() | 設置指定套接字的參數(端口復用) |
| s.getsockopt()? | 返回指定套接字的參數 |
| ? | |
| s.close() | 關閉套接字 |
| 面向鎖的套接字方法 | |
| s.setblocking() | 設置套接字的阻塞(True)與非阻塞模式(False)? ***** |
| s.settimeout() | 設置阻塞套接字操作的超時時間? accept()的等待時間 |
| s.gettimeout()? | 得到阻塞套接字操作的超時時間 |
| ? | ? |
| 面向文件的套接字的函數 | |
| s.fileno()?? | 套接字的文件描述符 |
| s.makefile() | 創建一個與該套接字相關的文件 |
?
3、驗證客戶端合法性 場景:如果別人知道了你的服務器的IP,那么他就可以使用掃端口的方式去連接上你的服務器,因為我們都知道,端口的范圍是0-65535, 那么別人知道了你的服務器IP后,就可以循環掃這些端口,就可以連接上你的服務,你服務器所進行的一些操作,比如一些數據的傳輸, 就會被別人所獲取,所以這個時候就需要驗證客戶端的合法性。代碼 服務端: import os import hmac import socketdef auth(conn):msg = os.urandom(32) # 生成一個32位的隨機的字節碼(urandom生成的字節碼就是bytes類型的)conn.send(msg) # 把這個隨機的字節碼發送到client端# hmac接收兩個參數,第一個參數相當于hashlib的鹽,第二個參數是我們隨機生成的字節碼,兩個參數都是bytes類型result = hmac.new(secret_key,msg) # 處理這個隨機字節碼,socket_key是鹽res = result.hexdigest() # 得到結果(字符串)client_digest = conn.recv(1024) # 接收client端處理的結果if res == client_digest.decode('utf-8'):print('合法的連接') # 對比成功可以繼續通信return Trueelse:print('不合法連接') # 不成功return Falsesecret_key = b'xiaoming' # hmac的鹽 sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8080)) sk.listen() conn,addr = sk.accept() if auth(conn):msg = conn.recv(1024) # True正常的和client端進行溝通print(msg.decode('utf-8'))conn.close() else:conn.close() # False 直接關閉和這個客戶端的連接 sk.close()客戶端: import hmac import socketdef auth(sk):msg = sk.recv(32) # 接收服務端傳來的隨機字節碼result = hmac.new(secret_key,msg) # 處理接收到的隨機字節碼res = result.hexdigest() # 得到結果sk.send(res.encode('utf-8')) # 把結果發回給服務端,讓服務端進行驗證 secret_key = b'xiaoming' # 因為鹽是程序員自己設置的,那么程序員寫的客戶端肯定知道自己的鹽是什么 sk = socket.socket() sk.connect(('127.0.0.1',8080)) auth(sk) sk.send(b'connect success') # 進行其他正常的和server端的溝通 sk.close()二、socketserver 正常服務端的socket,每一次只能連接一個客戶端,只有跟當前客戶端斷開連接后才能和下一個客戶端連接, 而用socketserver可以跟多個客戶端同時連接(并發)。服務端: import socketserver # tcp協議的server端就不需要導入socket class Myserver(socketserver.BaseRequestHandler): # 繼承socketserver.BaseRequestHandler這個類def handle(self): # 必須繼承handle方法并重寫conn = self.request # self.request就是客戶端的對象while True: # 和客戶端進行交互conn.send(b'helloworld')print(conn.recv(1024).decode('utf-8'))# 設置allow_reuse_address允許服務器重用地址 socketserver.TCPServer.allow_reuse_address = True# 創建一個對象server,綁定ip和端口,相當于sk = socket.socket() sk.bind(('127.0.0.1',8888))這兩步的結合 server = socketserver.ThreadingTCPServer(('127.0.0.1',8888),Myserver)# 讓server一直運行下去,除非強制停止程序 server.serve_forever()客戶端: import socket sk = socket.socket() sk.connect(('127.0.0.1',8888)) while True:ret = sk.recv(1024)print(ret.decode('utf-8'))sk.send(b'hiworld') sk.close()解釋為什么一定要重寫handler方法: Myserver這個類沒有__init__方法,那么它就會去繼承使用父類BaseRequestHandler的__init__方法 看看BaseRequestHandler源碼: class BaseRequestHandler:def __init__(self, request, client_address, server):self.request = request # 獲取客戶端的連接(對象),設置為自己的屬性self.client_address = client_address # 客戶端的地址self.server = server self.setup()try:self.handle() # 初識化對象的時候執行handler方法finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):pass總結: 也就是說,子類繼承了父類的__init__方法,這個方法里面已經取到了客戶端的對象conn,和地址addr, 并且初始化的時候調用了handler方法,但是父類的handler方法并沒有實現任何功能,所以子類應該重寫handler方法便于與客戶端交互。實例:上傳文件server.pyimport json import struct import socketserver import operate_handlerclass MyFTP(socketserver.BaseRequestHandler):def handle(self):conn = self.requestlength = conn.recv(4)length = struct.unpack('i',length)[0]operate = (conn.recv(length)).decode('utf-8')operate_dic = json.loads(operate)opt = operate_dic['operate']usr = operate_dic['user']print(opt,usr)getattr(operate_handler,opt)(conn,usr)socketserver.TCPServer.allow_reuse_address = True server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyFTP) server.serve_forever()operate_handler.pyimport os import json import structbase_path = r'E:\PythonProject\ftp\server\root'def upload(conn,usr):fileinfo_len = conn.recv(4)fileinfo_len = struct.unpack('i',fileinfo_len)[0]fileinfo = (conn.recv(fileinfo_len)).decode('utf-8')fileinfo = json.loads(fileinfo)file_path = os.path.join(base_path,usr,fileinfo['filename'])file_path = os.path.abspath(file_path)with open(file_path,'wb') as f:while fileinfo['filesize']:content = conn.recv(20480)fileinfo['filesize'] -= len(content)f.write(content)print('接收完畢')client.pyimport os import json import struct import socket# 發送信息 def my_send(sk,operate_info):b_optinfo = (json.dumps(operate_info)).encode('utf-8')num = struct.pack('i',len(b_optinfo))sk.send(num)sk.send(b_optinfo)sk = socket.socket() sk.connect(('127.0.0.1',9000))# [登錄,注冊,退出]# 要進行的操作 operate_info = {'operate':'upload','user':'xiaoming'} my_send(sk,operate_info)# 選擇一個文發送到server端 file_path = r'F:\電影\電影\荒野生存.mp4'# 發送文件信息 file_name = os.path.basename(file_path) file_size = os.path.getsize(file_path) file_info = {'filename':file_name,'filesize':file_size} my_send(sk,file_info)# server端接收寫入 with open(file_path,'rb') as f:while file_size:content = f.read(20480)file_size -= len(content)sk.send(content) print('上傳完畢') sk.close()
?
轉載于:https://www.cnblogs.com/Zzbj/p/9664404.html
總結
以上是生活随笔為你收集整理的socketserver和socket的补充(验证客户端合法性)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 脉脉怎么加评论区的好友
- 下一篇: [转]xargs详解