[英]Python - connecting sockets, client/server agnostic
I want 2 processes to communicate on a given port without either one having a defined client or server role. 我希望2个进程在给定端口上进行通信,而没有一个进程具有定义的客户端或服务器角色。 Either of the processes may be running alone.
这两个进程都可能单独运行。 Either may stop and restart at any time, in any order.
可以随时以任何顺序停止并重新启动。 When they are both running they need to communicate (when only one is running, communication is just discarded).
当它们都在运行时,它们需要进行通信(当仅一个正在运行时,通信将被丢弃)。
I want non-blocking sockets and Windows/Linux support. 我想要非阻塞套接字和Windows / Linux支持。
Here's a rather crude class that actually works to some extent, which might get you started. 这是一个相当粗糙的类,实际上在某种程度上有效,这可能会让您入门。
The main trick here is not to bother with listen
at all: these are pure peer to peer connections, fully specified by the <local-addr, remote-addr> pair. 这里的主要技巧是根本不用去理会
listen
:它们是纯对等连接,完全由<local-addr,remote-addr>对指定。
Note that the sockets are left in non-blocking mode. 请注意,套接字处于非阻塞模式。 I caught the
recv
exception but there can be a send
one as well (plus, you get broken-pipe errors when sending to a dead peer, etc). 我捕获了
recv
异常,但也可能有一个send
异常(另外,在发送给失效的对等节点时,您会遇到管道破裂错误等)。 You'll also need to handle EOF-from-terminated-peer (when recv
returns ''
instead of failing with EAGAIN
). 您还需要处理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)
Run with: $ python peerish.py -p 9001 -r 9002 & python peerish.py -p 9002 -r 9001 &
for instance. 例如:
$ python peerish.py -p 9001 -r 9002 & python peerish.py -p 9002 -r 9001 &
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.