- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想在 app.py 上使用异步创建两个协议(protocol)(TcpClient 和 UdpServer),其中 TcpClient 将与 server.py 和用作 UDP 服务器的 UdpServer 建立持久连接:
我需要的:
a) 两个协议(protocol)进行通信:互相调用方法。这仅适用于第一次连接。如果 TcpClient 重新连接,它不能再次发送字符串“send to tcp”。来自 UdpServer。我检查了 print(self)
并且 TcpClient 创建了一个新实例,旧实例仍然存在但没有连接,但我不知道如何重构它。我认为我以错误的方式使用了 asyncio。
b) 当TcpClient 断开与server.py 的连接时,等待5s 再尝试重连,以此类推。我尝试使用 asyncio 的 call_later()
来做到这一点,但我认为有一种 native 方法可以做到这一点,而不是一种技巧。
c) 当我启动 app.py 时,如果 TcpClient 无法连接,我想在 5 秒后尝试重新连接,依此类推。我不知道该怎么做。
这里是我对 app.py server.py 的示例测试。 server.py 仅用于测试 - 这将是另一种语言。
只是说说我的尝试:
1) 当我启动 app.py 并且 server.py 已关闭时,app.py 不要重试。
2) 当 app.py 连接到 server.py 并且服务器关闭并快速启动时,TcpClient 重新连接,但我无法在新实例上使用更多方法相互连接并发送字符串“发送到 tcp”。到 server.py,只是旧的,没有更多的连接。
3) 如果我使用 asyncio.async()
而不是 run_until_complete()
,我将无法调用来自其他协议(protocol)的方法。
我把 app.py 和 server.py 放在这里,所以你可以复制并运行以进行测试。
我使用 ncat localhost 9000 -u -v
发送字符串“send to tcp.”。此字符串需要在 UdpServer 类上打印并传递给 TcpClient 类上的方法 send_data_to_tcp,此方法会将字符串发送到 server.py。 <- 这在第一次重新连接 tcpClient 后不起作用。
我使用的是 Python 3.4.0。
非常感谢。
import asyncio
#TCP client
class TcpClient(asyncio.Protocol):
message = 'Testing'
def connection_made(self, transport):
self.transport = transport
self.transport.write(self.message.encode())
print('data sent: {}'.format(self.message))
server_udp[1].tcp_client_connected()
def data_received(self, data):
self.data = format(data.decode())
print('data received: {}'.format(data.decode()))
if self.data == 'Testing':
server_udp[1].send_data_to_udp(self.data)
def send_data_to_tcp(self, data):
self.transport.write(data.encode())
def connection_lost(self, exc):
msg = 'Connection lost with the server...'
info = self.transport.get_extra_info('peername')
server_udp[1].tcp_client_disconnected(msg, info)
#UDP Server
class UdpServer(asyncio.DatagramProtocol):
CLIENT_TCP_TIMEOUT = 5.0
def __init__(self):
self.client_tcp_timeout = None
def connection_made(self, transport):
print('start', transport)
self.transport = transport
def datagram_received(self, data, addr):
self.data = data.strip()
self.data = self.data.decode()
print('Data received:', self.data, addr)
if self.data == 'send to tcp.':
client_tcp[1].send_data_to_tcp(self.data)
def connection_lost(self, exc):
print('stop', exc)
def send_data_to_udp(self, data):
print('Receiving on UDPServer Class: ', (data))
def connect_client_tcp(self):
coro = loop.create_connection(TcpClient, 'localhost', 8000)
#client_tcp = loop.run_until_complete(coro)
client_tcp = asyncio.async(coro)
def tcp_client_disconnected(self, data, info):
print(data)
self.client_tcp_info = info
self.client_tcp_timeout = asyncio.get_event_loop().call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)
def tcp_client_connected(self):
if self.client_tcp_timeout:
self.client_tcp_timeout.cancel()
print('call_later cancel.')
loop = asyncio.get_event_loop()
#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000))
#server_udp = asyncio.Task(coro)
server_udp = loop.run_until_complete(coro)
#TCP client
coro = loop.create_connection(TcpClient, 'localhost', 8000)
#client_tcp = asyncio.async(coro)
client_tcp = loop.run_until_complete(coro)
loop.run_forever()
import asyncio
class EchoServer(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
print('data received: {}'.format(data.decode()))
self.transport.write(data)
# close the socket
#self.transport.close()
#def connection_lost(self):
# print('server closed the connection')
loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServer, 'localhost', 8000)
server = loop.run_until_complete(coro)
print(server)
print(dir(server))
print(dir(server.sockets))
print('serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
print("exit")
finally:
server.close()
loop.close()
最佳答案
你真的只需要一些小的修复。首先,我编写了一个协程来处理连接重试:
@asyncio.coroutine
def do_connect():
global tcp_server # Make sure we use the global tcp_server
while True:
try:
tcp_server = yield from loop.create_connection(TcpClient,
'localhost', 8000)
except OSError:
print("Server not up retrying in 5 seconds...")
yield from asyncio.sleep(5)
else:
break
然后我们用它来启动一切:
loop = asyncio.get_event_loop()
#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000))
server_udp = loop.run_until_complete(coro)
#TCP client
loop.run_until_complete(do_connect())
loop.run_forever()
下一部分是在 app.py 启动后处理服务器关闭/恢复。我们需要修复 tcp_client_disconnected
和 connect_client_tcp
以正确处理:
def connect_client_tcp(self):
global client_tcp
task = asyncio.async(do_connect())
def cb(result):
client_tcp = result
task.add_done_callback(cb)
def tcp_client_disconnected(self, data, info):
print(data)
self.client_tcp_info = info
self.client_tcp_timeout = loop.call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)
有趣的部分是connect_client_tcp
。您的原始版本有两个问题:
您将 client_tcp
直接分配给 asyncio.async(coro)
的结果,这意味着 client_tcp
分配给了一个asyncio.Task
。那不是你想要的;您希望将 client_tcp
分配给已完成的 asyncio.Task
的结果。我们通过使用 task.add_done_callback
将 client_tcp
分配给 Task
完成后的结果来实现这一点。
您忘记了方法顶部的 global client_tcp
。否则,您只是在创建一个名为 client_tcp
的局部变量,它在 connect_client_tcp
结束时被丢弃。
一旦这些问题得到解决,我就可以运行 app.py
,随时启动/停止 serv.py
,但始终能看到所有消息正确传递当所有三个组件一起运行时,从 ncat
到 serv.py
。
这是完整的 app.py
,便于复制/粘贴:
import asyncio
#TCP client
class TcpClient(asyncio.Protocol):
message = 'Testing'
def connection_made(self, transport):
self.transport = transport
self.transport.write(self.message.encode())
print('data sent: {}'.format(self.message))
server_udp[1].tcp_client_connected()
def data_received(self, data):
self.data = format(data.decode())
print('data received: {}'.format(data.decode()))
if self.data == 'Testing':
server_udp[1].send_data_to_udp(self.data)
def send_data_to_tcp(self, data):
self.transport.write(data.encode())
def connection_lost(self, exc):
msg = 'Connection lost with the server...'
info = self.transport.get_extra_info('peername')
server_udp[1].tcp_client_disconnected(msg, info)
#UDP Server
class UdpServer(asyncio.DatagramProtocol):
CLIENT_TCP_TIMEOUT = 5.0
def __init__(self):
self.client_tcp_timeout = None
def connection_made(self, transport):
print('start', transport)
self.transport = transport
def datagram_received(self, data, addr):
self.data = data.strip()
self.data = self.data.decode()
print('Data received:', self.data, addr)
if self.data == 'send to tcp.':
client_tcp[1].send_data_to_tcp(self.data)
def connection_lost(self, exc):
print('stop', exc)
def send_data_to_udp(self, data):
print('Receiving on UDPServer Class: ', (data))
def connect_client_tcp(self):
global client_tcp
coro = loop.create_connection(TcpClient, 'localhost', 8000)
task = asyncio.async(do_connect())
def cb(result):
client_tcp = result
task.add_done_callback(cb)
def tcp_client_disconnected(self, data, info):
print(data)
self.client_tcp_info = info
self.client_tcp_timeout = loop.call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)
def tcp_client_connected(self):
if self.client_tcp_timeout:
self.client_tcp_timeout.cancel()
print('call_later cancel.')
@asyncio.coroutine
def do_connect():
global client_tcp
while True:
try:
client_tcp = yield from loop.create_connection(TcpClient, 'localhost', 8000)
except OSError:
print("Server not up retrying in 5 seconds...")
yield from asyncio.sleep(1)
else:
break
loop = asyncio.get_event_loop()
#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000))
server_udp = loop.run_until_complete(coro)
#TCP client
loop.run_until_complete(do_connect())
loop.run_forever()
关于python - 如何重新连接 asyncio 上的套接字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25998394/
我使用下拉菜单提供一些不同的链接,但我希望这些链接在同一选项卡中打开,而不是在新选项卡中打开。这是我找到的代码,但我对 Javascript 非常缺乏知识 var urlmenu = docume
我对 javascript 不太了解。但我需要一个垂直菜单上的下拉菜单,它是纯 JavaScript,所以我从 W3 复制/粘贴脚本:https://www.w3schools.com/howto/t
我已经坐了 4 个小时,试图让我的导航显示下 zipper 接垂直,但它继续水平显示它们。我无法弄清楚为什么会发生这种情况或如何解决它。 如果有人能告诉我我做错了什么,我将不胜感激。我有一个潜移默化的
我正在尝试创建选项卡式 Accordion 样式下拉菜单。我使用 jQuery 有一段时间了,但无法使事件状态达到 100%。 我很确定这是我搞砸的 JS。 $('.service-button').
对于那些从未访问过 Dropbox 的人,这里是链接 https://www.dropbox.com/ 查看“登录”的下拉菜单链接。我如何创建这样的下 zipper 接? 最佳答案 这是 fiddle
我正在制作一个 Liferay 主题,但我在尝试设计导航菜单的样式时遇到了很多麻烦。我已经为那些没有像这样下拉的人改变了导航链接上的经典主题悬停功能: .aui #navigation .nav li
如果您将鼠标悬停在 li 上,则会出现一个下拉菜单。如果您将指针向下移至悬停时出现的 ul,我希望链接仍然带有下划线,直到您将箭头从 ul 或链接移开。这样你就知道当菜单下拉时你悬停在哪个菜单上。 知
我有一个带有多个下拉菜单的导航栏。因此,当我单击第一个链接时,它会打开下拉菜单,但是当我单击第二个链接时,第一个下拉菜单不会关闭。 (所以如果用户点击第二个链接我想关闭下拉菜单) // main.js
我正在尝试制作一个导航下拉菜单(使用 Bootstrap 3),其中链接文本在同一行上有多个不同的对齐方式。 在下面的代码中,下拉列表 A 中的链接在 HTML 中有空格字符来对齐它们,但是空白被忽略
我希望有人能帮我解决这个 Bootstrap 问题,因为我很困惑。 有人要求我在底部垂直对齐图像和其中包含图像的链接。 我面临的问题是他们还希望链接在链接/图像组合上具有 pull-right,这会杀
我正在构建一个 Rails 应用程序,并希望指向我的类的每个实例的“显示”页面的链接显示在“索引”页面的下拉列表中。我目前正在使用带有 options_from_collection_for_sele
我有以下 Bootstrap3 导航菜单 ( fiddle here )。我想设置“突出显示”项及其子链接与下拉列表 1 和 2 链接不同的链接文本(和悬停)的样式。我还希望能够以不同于 Highli
我对导航栏中的下拉菜单有疑问。对于普通的导航链接(无下拉菜单),我将菜单文本放在 H3 中,但是当我尝试对下 zipper 接执行相同操作时,箭头不在标题旁边,而是在标题下方。我决定用 span 替换
我是一名优秀的程序员,十分优秀!