I have Python client which opens a websocket connection to a server and subscribes to particular topic using STOMP protocol, subscription goes just fine as i see on the server all is fine. However, When the server publishes a few messages the client does not receive any.
Here are the codes used:
Client
# coding: utf-8
import websocket
import stomp
import stomper
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInByaW5jaXBhbF9uYW1lIjoiYWRtaW4iLCJpc3MiOiJBdGhlbmEiLCJ1c2VydHlwZSI6IkxPQ0FMIiwiYW9zX3ZlcnNpb24iOiJldXBocmF0ZXMtNS4xMS1zdGFibGUiLCJyZWdpb24iOiJlbi1VUyIsImV4cCI6MTczNDI4MDI3NywidXVpZCI6ImI4MzhjOGRkLWI4NmQtNGNkZS05ZTE4LTUxM2E1OTk4ODhhYyIsImlhdCI6MTU3NjYwMDI3NywiYXV0aG9yaXRpZXMiOiJST0xFX0NMVVNURVJfQURNSU4sUk9MRV9NVUxUSUNMVVNURVJfQURNSU4sUk9MRV9VU0VSX0FETUlOLFJPTEVfQ0xVU1RFUl9WSUVXRVIiLCJqdGkiOiI1NTU1ZjEwZC04NGQ5LTRkZGYtOThhNC1mZmI1OTM1ZTQwZWEifQ.LOMX6ppkcSBBS_UwW9Qo2ieWZAGrKqADQL6ZQuTi2oieYa_LzykNiGMWMYXY-uw40bixDcE-aVWyrIEZQbVsvA"
headers = {"Authorization": "Bearer " + token}
uri = "ws://127.0.0.1:8765/notifications/websocket"
def on_msg(ws, msg):
print(msg)
def on_error(ws, err):
print(err)
def on_closed(ws):
print("#Closed#")
def on_open(ws):
sub = stomper.subscribe("/user/queue/alert", "MyuniqueId", ack="auto")
ws.send(sub)
headers = {"Authorization": "Bearer " + token}
websocket.enableTrace(True)
ws = websocket.WebSocketApp(uri, header=headers, on_message=on_msg, on_error=on_error, on_close=on_closed)
ws.on_open = on_open
ws.run_forever()
Code server uses to publish the message:
for (WatchesSubscription s : subscriptions) {
template.convertAndSendToUser(s.getSession().getUser(), destination, dto);
}
When i checked out the value of the above variables i saw that destination was as expected queue/alerts. I have java client to test out as well and it works just fine. I have even tried this by subscribing to /topic/alerts and sending to it via template.convertAndSend(/topic/alerts), here too i received nothing. I am a drawing a complete blank on this and would appreciate any sort of help!
After many days of hair pulling I finally figured out the reason and the fix!
The java client I used was
WebSocketStompClient stompClient = new WebSocketStompClient(transport);.The stompClient.connect(URL, webSocketHttpHeaders, sessionHandler); method implicitly sends a stomp CONNECT\n\n\x00\n
The Springboot server which has been configured for STOMP understands this as a connection request and responds with a CONNECT_ACK.
When this ACK is sent it also updates it's local UserRegistry with the new user. So the internal message broker knows that there is a user who has subscribed to so-and-so topic.
In my Python code, i had merely opened a Websocket connection and after that directly sent a SUBSCRIBE message. So the broker never got a CONNECT so the user was never stored! This resulted in the messages later on being published to be merely discarded by the broker.
The fix was to send a CONNECT\n\n\x00\n after opening up the connection and before the subscription. Here is the code:
def on_open(ws):
#The magic happens here!
ws.send("CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n")
sub = stomper.subscribe("/user/queue/alert", "MyuniqueId", ack="auto")
ws.send(sub)
Related
This question already has an answer here:
aiosmtpd - python smtp server
(1 answer)
Closed 3 months ago.
I had "successfully" made an SMTP server. The code works fine connecting to SMTP clients. But it is neither able to recieve emails nor send it. I tried with various test servers and also the standard gmail/yahoo etc.
Here is the code:
# Copyright 2014-2021 The aiosmtpd Developers
# SPDX-License-Identifier: Apache-2.0
import asyncio
from asyncio.base_events import Server
import logging
import aiosmtpd
from aiosmtpd.controller import DEFAULT_READY_TIMEOUT, Controller
import ssl
from aiosmtpd.smtp import Envelope, Session
from smtplib import SMTP as SMTPCLient
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('cert.pem', 'privkey.pem')
class ExampleHandler():
async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
if address.endswith('#example.com'):
print('not relaying to that domain bro :(')
return '550 not relaying to that domain'
envelope.rcpt_tos.append(address)
print(address+" "+"is added to rcpt_tos")
# Make an envelope for the recipient with the same content.
return '250 OK'
# async def handle_EHLO(self, server, session, envelope):
# print('EHLO from %s' % envelope.mail_from)
# return '250-Hello, how are you?\n250-I am fine\n250 HELP'
async def handle_DATA(self, server, session, envelope):
print('Message from %s' % envelope.mail_from)
print('Message for %s' % envelope.rcpt_tos)
print('Message data:\n')
for ln in envelope.content.decode('utf8', errors='replace').splitlines():
print(f'> {ln}'.strip())
print()
print('End of message')
# Dump the contents of envelope.content to a file.
fi=open('./mailbox/firstletter.txt','w')
fi.write(envelope.content.decode('utf8', errors='replace'))
fi.close()
# print everything in DATA.
# Send the envelope to the recipient.
return '250 Message will be delivered'
#Define Relay server.
async def amain(loop):
cont = Controller(ExampleHandler(),hostname='x.x.x.x', port=25, server_hostname='Galam Limited',ready_timeout=5000)
# Combining ExampleHandler and Controller into a single Controller.
cont.start()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
loop = asyncio.get_event_loop()
loop.create_task(amain(loop=loop))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
You can test the server reachability . I am stuck and spent 2 whole days to no avail. The issue is definetely not connectivity, I put the port 25 open. Made sure there are no external issues with godaddy either. Any help will be appreicated.
Edit:1
A quick peak at the wire shark data shows absolutely no packet is being transmitted to the outside when I run the client script.
Here is the clinet script I used for testing.
from smtplib import SMTP as Client
from aiosmtpd.controller import Controller
class controller:
hostname='192.168.1.33'
port=25
client = Client(controller.hostname, controller.port)
r = client.sendmail('a#galam.in', ['tester#192.168.1.200'], """\
From: Anne Person <anne#galam.in>
To: Bart Person <tester#192.168.1.200>
Subject: A test
Message-ID: <ant>
Hi Bart, this is Anne.
""")
SMTP 250 code means that a successful connection has been established however the remote host you are sending mails to might have categorized the domain the mail is being sent from as not legitimate.
This can happen if your domain is not authenticated/verified.
You can relay your messages through a trusted SMTP service like sendgrid
You can also check if your domain is verified by sending a mail from your service to check-auth#verifier.port25.com. Port25 is an automated tool that verified your DNS records, SPF records etc.
Hope this works for you!
Help me figure out how to implement it correctly.
The bottom line: The client is charging stations that connect, open a socket and send messages to the server and receive responses.
Server - listens to the port, sees the connected station, receives messages and sends responses to them.
Question: When the client connects and sends headers, I can send a message to the client. But I need to periodically send messages to the client that keeps the socket open, I don't understand how to implement this. Can someone tell me?
sample sending code:
charge_point_id = path.strip('/')
cp = client_main(charge_point_id, websocket)
logging.info(charge_point_id)
print(charge_point_id)
print(path)
await websocket.send(json.dumps([2,"222", "GetLocalListVersion", {}]))
await cp.start()
example of receiving a message from a client:
class client_main(cp):
errors = False
if not errors:
#on('BootNotification')
def on_boot_notitication(self, charge_point_vendor, charge_point_model,charge_point_serial_number,firmware_version,
meter_type, **kwargs):
return call_result.BootNotificationPayload(
status="Accepted",
current_time=date_str.replace('+00:00','Z'),
interval=60
)
in this case, the charging station according to the ocpp protocol opens the connection and keeps it open, it should be possible to somehow write to him
how do i send a message to the client? My example:
#on('Heartbeat')
def on_getlocallistversion(self):
await self.route_message(json.dumps([2,"222","GetLocalListVersion",{}]))
def on_hearbeat(self):
return call_result.HeartbeatPayload(
current_time=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')+"Z"
)
I get an error:
await self.route_message(json.dumps([2,"222",
"GetLocalListVersion",{}]))
This is impossible to do. You can send a message only when the client is connected.
I am trying to develop a "reverse proxy" type of thing, and I am almost done. However, I have ran into an issue. I have two servers. The main server running the actual source (server A) and the proxy server which clients will connect to (server B).
Basically, my design is as follows:
After client connects to Server B (the proxy server)...
Client begins sending their packets to Server B.
Server B sends their packets to Server A
Server A receives their packet, parses it appropriately (this is the actual source), and then sends response packet(s) back to Server B
Server B receives the packet response from Server A, and sends it back to the Client.
So far, it is working fine. However, there is one major issue. The asynchronous loop can only receive one packet from Server A (the source server) per client packet. So if Server B sends one packet to Server A, Server B will only be able to receive one response packet from Server A, even if Server A wants to respond with multiple packets. I believe the read function is blocking.
I have a few files. I am running everything inside this loop:
import asyncio
asyncio.run(server.loop())
And then this is the actual loop (it is a longer file, but I removed redundant information):
async def loop(self):
self.server = await asyncio.start_server(self.init, self.settings['proxy']['host'], self.settings['proxy']['ports'][self.type])
async with self.server:
print("~Listening # {%s:%s}" % (self.settings['proxy']['host'], str(self.settings['proxy']['ports'][self.type])))
print()
await self.server.start_serving()
await self.server.serve_forever()
async def init(self, r, w):
await cli.client(self, r, w).connect()
As you can see it calls a client class, that client class contains the loop for the actual proxy file. It receives packets from the client, sends it to Server A, receives Server A's response, and sends it back to the client. This is the client class (redundant information removed)
async def connect(self):
self.ip = self.w.get_extra_info('peername')
self.tag = ("[%s:%s]" % (self.ip[0], str(self.ip[1])))
self.dest['r'], self.dest['w'] = await asyncio.open_connection(host=self.host, port=self.port)
print("NEW_CLIENT::%s" % (self.tag))
while(not self.w.is_closing()):
try:
data = await self.r.readuntil(separator=b'\x00')
if data:
await self.forward(data)
else:
await self.disconnect()
except asyncio.IncompleteReadError:
await self.disconnect()
async def forward(self, data):
#data = data.decode("utf-8")[:-1]
print("<= SEND_DEST::%s %s" % (self.tag, data.decode("utf-8")[:-1]))
self.dest['w'].write(data)
await self.dest['w'].drain()
data = await self.dest['r'].readuntil(separator=b'\x00')
print("=> RECV_DEST::%s => %s" % (self.tag, data.decode("utf-8")[:-1]))
if data:
self.w.write(data)
async def disconnect(self):
print("[Disconnect Client]: %s:%s" % (self.ip[0], str(self.ip[1])))
self.w.close()
So far, everything works as intended. The client sends a packet to this proxy file (server B), the packet is sent to the main source (server A), the main source sends back packets to the proxy file (server B), and then finally server B sends server A's response back to the client. It is a working proxy script.
The Problem: With this reverse proxy, I can only receive one line at a time from the main Server A. So basically, if the client sends Server B 1 packet, this remote proxy only reads 1 response packet from Server A, even if Server A sends several response packets back. So basically, I can only read one line at a time, in the forward() function.
I can't really figure out what to do here, except maybe make another loop for the reading process? I don't know how I would do that though. Maybe use run_in_executor?
I am connecting to an external VOLTTRON instance. I am not getting a response from the connection. What's the issue?
I am writing a simple python script to connect to an external platform and retrieve the peers. If I get the serverkey, clientkey, and/or publickey incorrect I don't know how to determine which is the culprit, from the client side. I just get a gevent timeout. Is there a way to know?
import os
import gevent
from volttron.platform.vip.agent import Agent
secret = "secret"
public = "public"
serverkey = "server"
tcp_address = "tcp://external:22916"
agent = Agent(address=tcp_address, serverkey=serverkey, secretkey=secret,
publickey=public)
event = gevent.event.Event()
greenlet = gevent.spawn(agent.core.run, event)
event.wait(timeout=30)
print("My id: {}".format(agent.core.identity))
peers = agent.vip.peerlist().get(timeout=5)
for p in peers:
print(p)
gevent.sleep(3)
greenlet.kill()
The short answer: no, the client cannot determine why its connection to the server failed. The client will attempt to connect until it times out.
Logs and debug messages on the server side can help troubleshoot a connection problem. There are three distinct messages related to key errors:
CURVE I: cannot open client HELLO -- wrong server key?
Either the client omit the server key, the client used the wrong server key, or the server omit the secret key.
CURVE I: cannot open client INITIATE vouch
Either the client omit the public or secret key, or its public and secret keys don't correspond to each other.
authentication failure
The server key was correct and the secret and public keys are valid, but the server rejected the connection because the client was not authorized to connect (based on the client's public key).
The first two messages are printed by libzmq. To see the third message volttron must be started with increased verboseness (at least -v).
Here is a simple ZMQ server-client example you can use to test some of these scenarios:
Server:
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.curve_server = 1
socket.curve_secretkey = "mW4i2O{kmcOXs9q>UP0(no4-Sp1r(p>vK?*NFwV$"
# The corresponding public key is "krEC0>hsx+o4Jxg2yvitCOVwr2GF85akNIsUdiH5"
socket.bind("ipc://test123")
while True:
msg = socket.recv()
new_msg = "I got the message: {}".format(msg)
print(new_msg)
socket.send(new_msg)
Client:
import zmq
pub, sec = zmq.curve_keypair()
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.curve_secretkey = sec
socket.curve_publickey = pub
socket.curve_serverkey = "krEC0>hsx+o4Jxg2yvitCOVwr2GF85akNIsUdiH5"
socket.connect("ipc://test123")
socket.send(b'Hello')
msg = socket.recv()
print("From the server: {}".format(msg))
I'm trying to get a Python client talking to a Node.js server using Socket.io 0.7, by sending a custom event to the server.
Based on the Socket.io reference I have found on GitHub, and the following WebSocket Python library.
Here's is my code so far:
Node server
io.sockets.on('connection', function (socket) {
socket.on('newimg', function(data) {
console.log(data);
});
});
Python client
def handshake(host, port):
u = urlopen("http://%s:%d/socket.io/1/" % (host, port))
if u.getcode() == 200:
response = u.readline()
(sid, hbtimeout, ctimeout, supported) = response.split(":")
supportedlist = supported.split(",")
if "websocket" in supportedlist:
return (sid, hbtimeout, ctimeout)
else:
raise TransportException()
else:
raise InvalidResponseException()
try:
(sid, hbtimeout, ctimeout) = handshake(HOSTNAME, PORT) #handshaking according to socket.io spec.
Except Exception as e:
print e
sys.exit(1)
ws = websocket.create_connection("ws://%s:%d/socket.io/1/websocket/%s" % (HOSTNAME, PORT, sid))
print ws.recv()
ws.send("2::")
ws.send("5:1::{'name':'newimg', 'args':'bla'}")
print ws.recv()
print "Closing connection"
ws.close()
Node console output
debug - client authorized
info - handshake authorized 12738935571241622933
debug - setting request GET /socket.io/1/websocket/12738935571241622933
debug - set heartbeat interval for client 12738935571241622933
debug - client authorized for
debug - websocket writing 1::
debug - websocket received data packet 2::
debug - got heartbeat packet
debug - websocket received data packet 5:1::{'name':'newimg', 'args':'bla'}
debug - acknowledging packet automatically
debug - websocket writing 6:::1
info - transport end
debug - set close timeout for client 12738935571241622933
debug - cleared close timeout for client 12738935571241622933
debug - cleared heartbeat interval for client 12738935571241622933
debug - discarding transport
Python console output
Done
1::
6:::1
Closing connection
Now it seems the socket event is not being triggered, despite the server responding with ACK. So the message is being correctly received but I am assuming, not formatted properly for socket.io to trigger an event.
I didn't think framing was necessary, however Archie1986 seems to disagree on his response to this: Socket.IO Client Library in Python
What might I be doing wrong here?
I wrapped rod's research into a barebones socket.io client library.
pip install -U socketIO-client
python
from socketIO_client import SocketIO
with SocketIO('localhost', 8000) as socketIO:
socketIO.emit('aaa')
socketIO.wait(seconds=1)
Resolved. I needed to use double quotes. Single quotes are not valid JSON. Woops.
ws.send("5:1::{'name':'newimg', 'args':'bla'}")
Becomes:
ws.send('5:1::{"name":"newimg", "args":"bla"}')