0

我希望 2 个进程在给定端口上进行通信,而没有一个具有已定义的客户端或服务器角色。任何一个进程都可以单独运行。任何一个都可以随时以任何顺序停止和重新启动。当它们都在运行时,它们需要通信(当只有一个运行时,通信被丢弃)。

我想要非阻塞套接字和 Windows/Linux 支持。

4

1 回答 1

1

这是一个相当粗略的课程,实际上在某种程度上有效,这可能会让您入门。

这里的主要技巧是根本不打扰listen:这些是纯粹的点对点连接,完全由 <local-addr, remote-addr> 对指定。

请注意,套接字处于非阻塞模式。我发现了recv异常,但也可能有异常send(另外,在发送到死节点时会出现管道中断错误等)。您还需要处理 EOF-from-terminated-peer (当recv返回''而不是失败时EAGAIN)。

import errno
import os
import select
import socket

class Peer(object):
    def __init__(self, local_addr, peer_addr):
        self._local_addr = local_addr
        self._peer_addr = peer_addr
        self._renew()
        self.reopen()

    def _renew(self):
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._sock.bind(self._local_addr)
        self._sock.setblocking(False)
        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._state = 'bound'

    def is_open(self):
        return self._state == 'open'

    def is_opening(self):
        return self._state == 'opening'

    def reopen(self):
        if self._state == 'open':
            raise ValueError('already open')
        if self._state == 'opening':
            raise ValueError('open in progress')
        print 'try connect to:', self._peer_addr
        error = self._sock.connect_ex(self._peer_addr)
        print 'result:', error
        if error == 0:
            self._state = 'open'
            print 'connected immediately'
        elif error in (errno.EINPROGRESS, errno.EINTR):
            self._state = 'opening'
            print 'connection in progress'
        else:
            raise socket.error(error, os.strerror(error))

    def _check_open(self):
        if self._state != 'opening':
            raise ValueError('improper call to _check_open')
        print 'check connect to:', self._peer_addr
        _, wfds, _ = select.select([], [self._sock], [])
        if len(wfds) == 0:
            # connection still in progress
            return
        # we have a result: fail or succeed, either way a result
        try:
            peer = self._sock.getpeername()
        except socket.error as err:
            print 'caught err:', err
            if err.errno == errno.ENOTCONN:
                print 'connection failed, no peer available'
                self.close()
                return
            raise
        print 'got a peer:', peer
        self._state = 'open'
        print 'connection finished'

    def close(self):
        if self._state in ('open', 'opening'):
            self._sock.close()
            self._renew()
            # self.reopen() - or leave to caller

    def send_if_connected(self, data):
        # to do: add check for send to dead peer, and if so, _renew etc
        if self._state == 'bound':
            self.reopen()
        if self._state == 'opening':
            self._check_open()
        if self._state == 'open':
            self._sock.send(data)

    def recv_if_connected(self):
        # to do: add check for send to dead peer, and if so, _renew etc
        if self._state == 'bound':
            self.reopen()
        if self._state == 'opening':
            self._check_open()
        if self._state == 'open':
            try:
                return self._sock.recv(1024)
            except socket.error as err:
                # still connected but no data avail
                if err.errno == errno.EAGAIN:
                    return ''
                raise
        else:
            return None

if __name__ == '__main__':
    import argparse
    import time

    parser = argparse.ArgumentParser(description='test Peer()')
    parser.add_argument('-l', '--localhost', default='')
    parser.add_argument('-p', '--port', type=int, default=9001)
    parser.add_argument('-R', '--remote-host', default='')
    parser.add_argument('-r', '--remote-port', type=int, default=9002)
    args = parser.parse_args()

    x = Peer((args.localhost, args.port), (args.remote_host, args.remote_port))
    for i in range(1, 10):
        print 'attempt to send %d' % i
        x.send_if_connected('send %d' % i)
        got = x.recv_if_connected()
        if got is not None:
            print 'got: "%s"' % got
        time.sleep(1)

运行:$ python peerish.py -p 9001 -r 9002 & python peerish.py -p 9002 -r 9001 &例如。

于 2013-06-27T02:39:01.497 回答