我想为服务器客户端使用RSA和AES加密编写自己的套接字类,并且对于服务器端,我需要重载accept()函数以返回(套接字,地址)对而不是(MySocket,地址)对。因此,这就是为什么我需要使用另一个套接字初始化MySocket类的原因。
这是我的代码的简化版本:
import socket as sock
class MySocket(sock.socket):
def __init__(self, socket_obj = None):
if socket_obj == None:
sock.socket.__init__(self, family = sock.AF_INET, type = sock.SOCK_STREAM)
else:
assert(socket_obj.family == sock.AF_INET and socket_obj.type == sock.SOCK_STREAM)
self = socket_obj
self.key = 'value'
my_socket = MySocket() # works fine
print('First worked')
my_socket = MySocket(socket_obj = sock.socket()) # falls with error
print('Second also worked')
执行此代码后,我得到一个错误:
Traceback (most recent call last):
File "./pyoverflow.py", line 19, in <module>
my_socket = MySocket(socket_obj = sock.socket()) # falls with error
File "./pyoverflow.py", line 13, in __init__
self.key = 'value'
AttributeError: 'socket' object has no attribute 'key'
据我了解,问题出在#9字符串self = socket_obj
中,但是在错误中搜索并没有给出任何结果。
答案 0 :(得分:2)
如果MySocket
是socket
的子类,则MySocket
实例是socket
实例; 1 >
仅分配socket
并没有任何用处。 socket
只是MySocket
方法内部的局部变量。因此,对于该方法的其余部分,当您对self = socket_obj
做某事时,您是对self
做某事,而不是对您自己的实际情况。如您所见,分配给它的__init__
属性失败。但是,即使它没有失败,您要做的就是修改self
,同时基本上不初始化自己。
在这里,要做有两种可能。
首先,您的socket_obj
可以拥有一个key
,而不是成为socket_obj
的{em},并提供与socket
类型,方法是将调用委派给其拥有的MySocket
。然后,您可以轻松选择是拥有一个新创建的套接字,还是拥有一个传入的套接字。类似这样的东西:
socket
在这里编写明确的委托人(无论是手动还是通过socket
循环并以编程方式创建它们)可能比socket
更好,但这可以使想法得到理解。
另一种选择是有效地完成ssl.SSLSocket
的工作。
套接字对象基本上只是一个围绕文件描述符的瘦包装器。您可以在套接字上调用detach
来窃取其fd,然后可以将该fd传递到socket
初始化程序中:
class MySocket:
def __init__(self, socket_obj = None):
if socket_obj is None:
socket_obj = socket.socket(self, family = sock.AF_INET, type = sock.SOCK_STREAM)
self._socket_obj = socket_obj
self.key = 'value'
def accept(self):
addr, s = self._socket_obj.accept()
return addr, MySocket(s)
def __getattr__(self, name):
if name.startswith('_'):
raise AttributeError(name)
return getattr(self._socket_obj, name)
inspect.getmembers(socket.socket)
实际执行的操作更为复杂(尽管不是 复杂的,可能值得阅读the source)。
但也请注意,其界面是不同的。在构造函数调用中,窃取另一个套接字的fd有点奇怪,因此__getattr__
将引发一个class MySocket(sock.socket):
def __init__(self, socket_obj=None):
if socket_obj is None:
super().__init__(family=sock.AF_INET, type=sock.SOCK_STREAM)
else:
super().__init__(fileno=socket_obj.detach())
self.key = 'value'
def accept(self):
addr, s = super().accept()
return addr, MySocket(s)
,而您则需要使用一个wrap_socket
函数。这可能是一个不太混乱的设计。
但是,如果您不需要具有所有不同包装套接字的不同Context对象的额外复杂性,则可能只需要SSLSocket
替代构造方法,而不是外部工厂函数。
1。实际上,在Python中,您有时可以 通过设置其SSLSocket(socket_obj)
属性来更改活动对象的类型。但这几乎总是一个坏主意,而这样做通常表明您的设计在某处出现了错误。