【问题标题】:Sending objects via sockets from Python to C/Fortran通过套接字从 Python 发送对象到 C/Fortran
【发布时间】:2017-04-04 11:24:44
【问题描述】:

以下代码是更大软件包的sn-ps。我想了解代码如何管理发送 numpy 数组、单元格等对象。我想知道的是代码似乎没有序列化对象,但它确实有效。为什么会起作用?

Python 客户端代码(sn-p):

class DriverSocket(socket.socket):

   def __init__(self, _socket_interface):
       super(DriverSocket,self).__init__(_sock=_socket_interface)


   def sendpos(self, pos, cell):
      """Sends the position and cell data to the driver.

      Args:
         pos: An array containing the atom positions.
         cell: A cell object giving the system box.

      Raises:
         InvalidStatus: Raised if the status is not Ready.
      """

      if (self.status & Status.Ready):
         try:
            self.sendall(Message("posdata"))
            self.sendall(cell.h)
            self.sendall(cell.ih)
            self.sendall(np.int32(len(pos)/3))
            self.sendall(pos)
         except:
            self.poll()
            return
      else:
         raise InvalidStatus("Status in sendpos was " + self.status)

在 C (sn-p) 中接收代码:

void open_socket_(int *psockfd, int* inet, int* port, char* host)
/* Opens a socket.

Note that fortran passes an extra argument for the string length, but this is
ignored here for C compatibility.

Args:
   psockfd: The id of the socket that will be created.
   inet: An integer that determines whether the socket will be an inet or unix
      domain socket. Gives unix if 0, inet otherwise.
   port: The port number for the socket to be created. Low numbers are often
      reserved for important channels, so use of numbers of 4 or more digits is
      recommended.
   host: The name of the host server.
*/

{
   int sockfd, portno, n;
   struct hostent *server;

   struct sockaddr * psock; int ssock;

   if (*inet>0)
   {  // creates an internet socket
      struct sockaddr_in serv_addr;      psock=(struct sockaddr *)&serv_addr;     ssock=sizeof(serv_addr);
      sockfd = socket(AF_INET, SOCK_STREAM, 0);
      if (sockfd < 0)  error("Error opening socket");

      server = gethostbyname(host);
      if (server == NULL)
      {
         fprintf(stderr, "Error opening socket: no such host %s \n", host);
         exit(-1);
      }

      bzero((char *) &serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
      serv_addr.sin_port = htons(*port);
      if (connect(sockfd, psock, ssock) < 0) error("Error opening socket: wrong host address, or broken connection");
   }
   else ...



void readbuffer_(int *psockfd, char *data, int* plen)
/* Reads from a socket.

Args:
   psockfd: The id of the socket that will be read from.
   data: The storage array for data read from the socket.
   plen: The length of the data in bytes.
*/

{
   int n, nr;
   int sockfd=*psockfd;
   int len=*plen;

   n = nr = read(sockfd,data,len);

   while (nr>0 && n<len )
   {  nr=read(sockfd,&data[n],len-n); n+=nr; }

   if (n == 0) error("Error reading from socket: server has quit or connection broke");
}

然后是使用 C-socket 代码的 Fortran 代码

CALL open_socket(socket, inet, port, host)
...
CALL readbuffer(socket, msgbuffer, nat*3*8)

而这个接收代码确实得到了一个二维数组等等。反之亦然。

【问题讨论】:

    标签: python c sockets fortran


    【解决方案1】:

    如果您尝试通过send/sendall 发送任意对象,您将看到一个异常,指出需要类似字节。所以想法很简单,你发送的 numpy 结构提供了类似字节的接口,转换为字节只留下原始二进制数据。

    最简单的实现是从bytes继承:

    class A: pass
    sock.sendall(A())  # exception
    
    class B(bytes): pass
    sock.sendall(B())  # no exception
    

    但是,numpy 是一个用 Python、Cython 和 C 编写的复杂框架。它们可能使用C API 来提供类似的功能。

    还值得注意的是,duck-typing 在这里不起作用:

    class C:
        def __bytes__(self):
            return bytes()
    
    sock.sendall(C())  # exception
    

    【讨论】:

    • 感谢您的回答。你的意思是你预计鸭子打字会起作用吗?此外,我编写了一个小的示例 python 代码,其中接收器和发送器是 python 套接字,并发送了一些 numpy 数组。它工作了,但是接收到的数据没有恢复,我得到了▯▯作为接收到的数据的值。
    • @Jadzia 我预计鸭式打字会起作用。至于您的示例代码,如果您有兴趣,我认为最好发布另一个问题。可能不是 numpy 中的所有内容都可以“按原样”通过网络发送。也有可能你的发送接收有错误,但我不敢坚持,因为我没有看到代码。
    • send/sendall 期望一个“类似字节”的对象,它是一个实现缓冲区协议 API 的对象。目前缓冲区协议只能在 C 中实现。这就是为什么您可以发送诸如 array.array 和 numpy.array 和 memoryview 之类的东西,它们不继承自 bytes,但确实实现了 C API。有一个开放票允许缓冲区协议的纯 python 实现,但它的优先级较低(已开放近 5 年)。 bugs.python.org/issue13797
    • @Dunes 和 Vovanrock:感谢您的回复,他们非常有帮助!同时,我还设法将这些 numpy 数组从 python 发送到 python 并恢复数据。这可以通过 np.fromstring(buffer, np.byte) 方法实现,该方法再次返回一个正确的 numpy 数组。
    • @Dunes:您的意思是 send/sendall 期望字符串(根据文档),它们也是类似字节的对象,对吧?
    猜你喜欢
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2012-03-13
    • 2019-04-21
    • 2014-01-11
    相关资源
    最近更新 更多