簡體   English   中英

Python-連接套接字,與客戶端/服務器無關

[英]Python - connecting sockets, client/server agnostic

我希望2個進程在給定端口上進行通信,而沒有一個進程具有定義的客戶端或服務器角色。 這兩個進程都可能單獨運行。 可以隨時以任何順序停止並重新啟動。 當它們都在運行時,它們需要進行通信(當僅一個正在運行時,通信將被丟棄)。

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

這是一個相當粗糙的類,實際上在某種程度上有效,這可能會讓您入門。

這里的主要技巧是根本不用去理會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 &

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM