socketserver和socket的补充(验证客户端合法性)
一、socket的补充
1、参数
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None) 参数说明:
family |
地址系列应为AF_INET(默认值ipv4),AF_INET6(ipv6),AF_UNIX,AF_CAN或AF_RDS。 |
type |
套接字类型应为SOCK_STREAM(默认值,tcp协议),SOCK_DGRAM(udp协议),SOCK_RAW或其他SOCK_常量之一。 |
proto |
协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。 |
fileno |
如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。 |
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() | 创建一个与该套接字相关的文件 |
官方文档对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
3、验证客户端合法性
场景:如果别人知道了你的服务器的IP,那么他就可以使用扫端口的方式去连接上你的服务器,因为我们都知道,端口的范围是0-65535, 那么别人知道了你的服务器IP后,就可以循环扫这些端口,就可以连接上你的服务,你服务器所进行的一些操作,比如一些数据的传输, 就会被别人所获取,所以这个时候就需要验证客户端的合法性。

# 服务端:
import os
import hmac
import socket def 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 True
else:
print('不合法连接') # 不成功
return False secret_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 socket def 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可以跟多个客户端同时连接(并发)。
1、TCP
服务器
import socketserver # tcp协议的server端就不需要导入socket
class Myserver(socketserver.BaseRequestHandler): # 继承socketserver.BaseRequestHandler这个类
def handle(self): # 必须继承handle方法并重写
print(self.client_address) # 客户端的IP和端口: ('127.0.0.1', 64491)
print(type(self.request)) # <class 'socket.socket'> 与客户端连接的socket套接字
# <socket.socket fd=500, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8888), raddr=('127.0.0.1', 64491)>
print(self.request) # 与客户端连接的套接字 conn = self.request # self.request就是客户端的对象
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)) msg = sk.recv(1024)
print(msg.decode('utf-8'))
sk.send(b'hiworld')
sk.close()
2、upd
服务器
import socketserver class Myserver(socketserver.BaseRequestHandler): # 继承socketserver.BaseRequestHandler这个类
def handle(self): # 必须继承handle方法并重写
print(self.client_address) # 客户端的IP和端口: ('127.0.0.1', 60575)
print(type(self.request)) # udp的时候,request是元组:<class 'tuple'>
print(self.request[0]) # 客户端的消息msg: b'dog'
print(self.request[1]) # udp套接字: <socket.socket fd=480, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8888)> sk = self.request[1]
sk.sendto(b'cat', self.client_address) # 设置allow_reuse_address允许服务器重用地址
socketserver.UDPServer.allow_reuse_address = True # 创建一个对象server,绑定ip和端口,相当于sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8888))这两步的结合
server = socketserver.ThreadingUDPServer(('127.0.0.1', 8888), Myserver) # 让server一直运行下去,除非强制停止程序
server.serve_forever()
客户端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.sendto(b'dog', ('127.0.0.1', 8888))
msg, addr = sk.recvfrom(1024)
print(msg.decode('utf-8'))
sk.close()
3、为什么一定要重写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):
pass def handle(self):
pass def finish(self):
pass 总结:
也就是说,子类继承了父类的__init__方法,这个方法里面已经取到了客户端的对象conn,和地址addr,
并且初始化的时候调用了handler方法,但是父类的handler方法并没有实现任何功能,所以子类应该重写handler方法便于与客户端交互。
4、实例:上传文件
服务器
# server.py import json
import struct
import socketserver
import operate_handler class MyFTP(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
length = 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.py import os
import json
import struct base_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.py import 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()
场景:如果别人知道了你的服务器的IP,那么他就可以使用扫端口的方式去连接上你的服务器,因为我们都知道,端口的范围是0-65535,
那么别人知道了你的服务器IP后,就可以循环扫这些端口,就可以连接上你的服务,你服务器所进行的一些操作,比如一些数据的传输, 就会被别人所获取,所以这个时候就需要验证客户端的合法性。
socketserver和socket的补充(验证客户端合法性)的更多相关文章
- Python之路(第三十四篇) 网络编程:验证客户端合法性
一.验证客户端合法性 如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现. 客户端验证的总的思路是将服务端随机产生的指定位数的字节发送到客 ...
- day 26 初识进程,验证客户端合法性
验证客户端合法性: # 1.需要认证 # 程序和用户打交道的时候才会用到用户认证 # 对所有的客户端进行统一的认证 # 我现在要做的事情 # 写一个server端 # 写一个client端 特殊的 符 ...
- SocketServer模块,hmac模块验证client合法性
hmac模块: 1.模块初识: import hmac # h = hmac.new() #括号里要给它连个bytes类型,一个是自定义的secret_key,一个是你想进行加密的bytes # 密文 ...
- python之路----验证客户端合法性
验证客户端链接的合法性 import os import hmac import socket secret_key = b'egg' sk = socket.socket() sk.bind(('1 ...
- socketv 验证客户端链接的合法性,socketserver
补充: send()与sendall() 在python socket编程中,有两个发送TCP的函数,send()与sendall(),区别如下: socket.send(string[, flags ...
- Day 35 验证客户端的合法性+socketserver
一 .getpeername和getsoketopt的用法 服务器端代码 import socket sk =socket.socket() sk.bind(('127.0.0.1',9000)) s ...
- python_网络编程hmac模块验证客户端的合法性
hmac模块: 比较两个函数的密钥是否一致: import hmac import os msg = os.urandom(32) #生成32位随机字节码 def wdc(): key = b'wdc ...
- python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)
先来回顾一下昨天的内容 黏包现象粘包现象的成因 : tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制 无边界 所有在连接建立的基础上传递的数据之间没有界限 收发消息很有可能不完全相 ...
- Python之socketserver模块和验证客户端链接的合法性
验证客户端链接的合法性 分布式系统中实现一个简单的客户端链接认证功能 #_*_coding:utf-8_*_ from socket import * import hmac,os secret_ke ...
随机推荐
- jsp内置对象-out对象
1.概念:隐含对象out是javax.servlet.jsp.JspWriter类的实例,是一个带缓冲的输出流,通过out对象实现服务器端向客户端输出字符串. 缓冲区的容量是可以设置的,甚至可以关闭, ...
- CPP内存检测
对C.C++的内存泄露.内存溢出等检查,经过这两天的查资料,总体来说可以使用Valgrind, AddressSanitizer, Dr.Memory等.其中Valgrind对程序运行速度影响较大,运 ...
- TabLayout您可能不知道的实用用法
一.修改点击的动画 函数:setUnboundedRipple 这是默认的点击的动画 我们用代码修改一下: mGlueTabLayout.setUnboundedRipple(true); 这是之后的 ...
- 安卓开发:UI组件-RadioButton和复选框CheckBox
2.5RadioButton 为用户提供由两个及以上互斥的选项组成的选项集. 2.5.1精简代码 在按钮变多之后,多次重复书写点击事件有些繁琐,我们在这里创建一个事件OnClick,每次点击时调用该事 ...
- 在phpstudy中安装并使用ThinkPHP 5
最近在慕课网学习 thinkphp,由于教师使用的是 MAC下的 MAMP 环境,而我使用的是 win7 的 phpstudy,区别不大,记录在这里,方便查询. 不同系统集成环境安装: m ...
- vue.js的手脚架vue-cli项目搭建的步骤
手脚架是什么? 众所周知,现在的前端项目发展得越渐越大,我们前端程序员要从0开始去搭建一套完整的项目很费时,所以这时候前端工程的手脚架就出现了. 我用得vue-cli也是其中之一,还有其他的我也说不清 ...
- asp.net --> 初识WCF
对于刚开始接触wcf的同志,可以有效的理解wcf的使用场景. 引用该文章(点击查看),简单的介绍wcf的使用.另一篇文章(点击查看),和上述文章内容差不多.
- python模块shutil
shutil.copyfileobj(fsrc, fdst,[ length]) 拷贝文件句柄,将类文件对象fsrc的内容复制到类文件对象fdst.如果给定整数长度,则为缓冲区大小.如果长度是负值意味 ...
- LeetCode算法题-Minimum Moves to Equal Array Elements(Java实现)
这是悦乐书的第233次更新,第246篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第100题(顺位题号是453).给定大小为n的非空整数数组,找到使所有数组元素相等所需的 ...
- 记录Vim常用命令
命令 简单说明 i 进入编辑模式,光标在原位置 I 进入编辑模式,光标在行首位置 o 从光标所在行,下面一行开始编辑 O 从光标所在行,上面一行开始编辑 a 从光标当前字符后编辑 A 从光标所在行的行 ...