如何使用标准套接字对象初始化“ MySocket”对象?

时间:2018-07-25 21:50:35

标签: python python-3.x

我想为服务器客户端使用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中,但是在错误中搜索并没有给出任何结果。

1 个答案:

答案 0 :(得分:2)

如果MySocketsocket的子类,则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)属性来更改活动对象的类型。但这几乎总是一个坏主意,而这样做通常表明您的设计在某处出现了错误。