Some information about my relevant-to-the-thread background:
Client-Server connection domain knowledge: Beginner
Python-programmer: Mid to advanced level
Problem I am facing:
connecting a Client to a Server using the OPCUA protocol (tool I am using: opcua python-library and VS-code editor).
How did the problem start?
I was attempting to use the node.get_value() method (where node = client.get_node("ns=2;i=2")). Then the error is: "None type does not have a method called 'send_request'". This made me think that the socket connection is not established. Therefore, I tried to establish a socket connection first, by using the UAClient.connect_socket() method, which lead me to the origin of the problem, which is the last command line in the following code:
My code:
from opcua import Server
from opcua import Client
from opcua import ua
from ua_client import *
from traceback import print_exc
from opcua import Client
name = "OPC_SIMULATION_SERVER"
addspace = server.register_namespace(name)
node = server.get_objects_node()
Param = node.add_object(addspace, "Parameters")
server = Server()
url = 'opc.tcp://127.0.0.1:4840' # opc server URL (connection works, checked in in command line with "ping")
server.start()
print("Server started at {}".format(url))
client = Client(url) # create client object
ref = client.uaclient # create UAClient object
# establish socket (to my understanding)
ref._uasocket = UASocketClient(ref._timeout, security_policy=ref.security_policy)
host = client.server_url.hostname
port = client.server_url.port
refsock = ref._uasocket
sock = socket.create_connection((host, port), timeout=refsock.timeout)
sock.settimeout(None) # for blocking (doesn’t cause the error)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
refsock._socket = ua.utils.SocketWrapper(sock)
# ERRATIC LINE: The following command gets stuck without an error/warning message
chunk = refsock._socket.socket.recv(8)
# \==================================================
I should note that initially I had no problem with getting the value from the node.
I'm trying to repurpose this script, I want the server to send messages to my client, and I want the client to be the one that 'reaches out' and makes the connection. As you can see, with the script I have, the client is the one reaching out but it's the one that can send the messages to the server. I've tried looking at other scripts, moving the send and handle function around, but I feel like my inexperience with sockets got me stumped. Any help would be appreicated.
Server:
import socket
import threading
HEADER = 64
PORT = 5430
SERVER = '192.110.100.189' # socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!END'
# defines the type of connection
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr):
print(f'[NEW CONNECTIONS] {addr} connected')
connected = True
while connected:
# the header variable here acts as a protocol to give us an idea of how many bytes are being sent
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg == DISCONNECT_MESSAGE:
break
print(f'{addr} | {msg}')
conn.send('msg recieved'.encode(FORMAT))
conn.close()
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}')
while True:
conn, addr = server.accept()
thread = threading.Thread(target = handle_client, args = (conn, addr))
thread.start()
print(f'[ACTIVE CONNECTIONS] {threading.activeCount() - 1}')
print('[STARTNG] Server is starting')
start()
Client:
import socket
HEADER = 64
PORT = 5430
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!END'
SERVER = '192.110.100.189'
ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message = msg.encode(FORMAT)
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
client.send(send_length)
client.send(message)
while True:
enter = input('CLIENT - ')
send(enter)
if enter == DISCONNECT_MESSAGE:
break
With mqtt subscribe client I am subscribing to lots of threads (over 6000) but not getting results that change on the fly. I'm lagging. Does mqtt give possibility to subscribe too many threads in parallel in background? loop_start would that be enough?
What should I pay attention to when subscribing to more topics?
import logging
import paho.mqtt.client as mqtt
import requests
import zmq
import pandas as pd
PORT=1351
def set_publisher():
context = zmq.Context()
socket_server = context.socket(zmq.PUB)
socket_server.bind(f"tcp://*:{PORT}")
return socket_server
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
#logging.warning(f"Connected with result code :: code : {rc}")
print(f"Connected with result code :: code : {rc}")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe(topics)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
msg = msg.payload
# logging.info(f"message:: {msg}\n")
print(f"message:: {msg}\n")
if msg:
publisher.send(f"{msg}")
def on_disconnect(client, userdata, rc):
if rc != 0:
# logging.warning(f"Unexpected disconnection :: code: {rc}")
print(f"Unexpected disconnection :: code: {rc}")
#todo: if rc is change hostname raise except
client = mqtt.Client(protocol=mqtt.MQTTv31, transport="tcp")
client.username_pw_set(******, password=******)
topics = [(f"topic{i}", 0) for i in 6000]
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect
if client.connect(hostname= *****, port= **** , keepalive=300) != 0:
# logging.info("Could not connect to MQTT Broker !")
print("Could not connect to MQTT Broker !")
client.loop_forever(timeout=3000)
You are describing a situation of compute power (either at the client or the broker or in-between) not sufficient to handle your scenario. It is a common occurrance and that is what performance testing is for: does your setup handle your scenario for your requirements? Capacity planning then expands that question to: ... in the future.
When running a socket server behind a network load balancer, with instances specified by IP, ...
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
server_sock.setblocking(False)
server_sock.bind(('', port))
server_sock.listen(socket.IPPROTO_TCP)
loop = asyncio.get_event_loop()
sock, adddress = await loop.sock_accept(server_sock)
... then...
print(adddress)
prints what appears to be a private IP of the network load balancer itself.
How can I determine the original client IP?
You can enable Proxy Protocol V2, and then write a function to parse the proxy protocol header [which gets sent ahead of incoming data]...
import asyncio
import collections
import socket
import struct
PP_V2_SIGNATURE = b'\r\n\r\n\x00\r\nQUIT\n'
PP_V2_COMMANDS = {
b'\x00': 'LOCAL',
b'\x01': 'PROXY',
}
PP_V2_VERSIONS = {
b'\x02': '2',
}
PP_V2_ADDRESS_FAMILIES = {
b'\x00': 'AF_UNSPEC',
b'\x01': 'AF_INET',
b'\x02': 'AF_INET6',
b'\x03': 'AF_UNIX',
}
PP_V2_PROTOCOLS = {
b'\x00': 'UNSPEC',
b'\x01': 'STREAM',
b'\x02': 'DGRAM',
}
PP_V2_ADDRESS_FORMATS = {
'AF_INET': '4B4BHH',
'AF_INET6': '16B16BHH',
'AF_UNIX': '108B108B',
}
ProxyProtocolV2Header = collections.namedtuple('ProxyProtocolV2Header', [
'version',
'command',
'protocol',
'address_family',
'address',
])
ProxyProtocolIpAddress = collections.namedtuple('ProxyProtocolIpAddress', [
'source_ip',
'source_port',
'dest_ip',
'dest_port',
])
async def proxy_protocol_header_recv(loop, sock):
# The header, itself has a header
header_format = '>12sccH'
header_length = 16
header_raw = await recv_num_bytes(loop, sock, header_length)
header_unpacked = struct.unpack(header_format, header_raw)
signature = header_unpacked[0]
version_and_command = header_unpacked[1][0]
protocol_and_address_family = header_unpacked[2][0]
address_length = header_unpacked[3]
if signature != PP_V2_SIGNATURE:
raise Exception('Incorrect proxy protocol signature')
version = PP_V2_VERSIONS[bytes([version_and_command >> 4])]
command = PP_V2_COMMANDS[bytes([version_and_command & 0x0f])]
protocol = PP_V2_PROTOCOLS[bytes([protocol_and_address_family & 0x0f])]
address_family = PP_V2_ADDRESS_FAMILIES[bytes([protocol_and_address_family >> 4])]
address_raw = await recv_num_bytes(loop, sock, address_length)
address_format = PP_V2_ADDRESS_FORMATS[address_family]
address = struct.unpack(address_format, address_raw[:struct.calcsize(address_format)])
return ProxyProtocolV2Header(
version=version,
command=command,
protocol=protocol,
address_family=address_family,
# For IPV6/Unix, you'll need to do something else
address=ProxyProtocolIpAddress(
source_ip='.'.join(str(part) for part in address[:4]),
source_port=address[8],
dest_ip='.'.join(str(part) for part in address[4:8]),
dest_port=address[9],
) if address_family == 'AF_INET' else None,
)
async def recv_num_bytes(loop, sock, num_bytes):
incoming_buf = bytearray(num_bytes)
incoming = memoryview(incoming_buf)
bytes_in_total = 0
while bytes_in_total != num_bytes:
bytes_just_in = await loop.sock_recv_into(sock, incoming[bytes_in_total:])
if bytes_just_in == 0:
raise Exception('Socket closed')
bytes_in_total += bytes_just_in
return incoming_buf
... which you you can use as, for example:
async def main(loop):
port = 5023
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
server_sock.setblocking(False)
server_sock.bind(('', port))
server_sock.listen(socket.IPPROTO_TCP)
while True:
print('Waiting for connection')
sock, adddress = await loop.sock_accept(server_sock)
header = await proxy_protocol_header_recv(loop, sock)
print(header.address.source_ip)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
with instances specified by IP
...you can't.
With instances (targets) specified by IP, Network Load Balancer doesn't have the ability to hook sufficiently deeply into the network infrastructure to preserve the client IP. When you specify instances by instance ID, the client IP is automatically preserved.
You can configure a target group so that you register targets by instance ID or IP address. If you specify targets using an instance ID, the source IP addresses of the clients are preserved and provided to your applications. If you specify targets by IP address, the source IP addresses are the private IP addresses of the load balancer nodes.
https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html
AWS load balancers place the client ip into the 'X-Forwarded-For' header
so simple query it by:
class CustomHandler(http.server.SimpleHTTPRequestHandler):
...
def do_GET(self):
aws_forwarded_ip = self.headers.get('X-Forwarded-For')
...
I have been working on MQTT protocol for quite some time. This protocol would be used by the organisation for sending acknowledgment messages to each client separately.
In the use-case, there is only 1 'publisher' client which publishes acknowledgment messages to 'subscriber' clients subscribed to their unique topics
To make sure that the broker is largely bug-free and can scale easily in future, I have been trying to test Emqx and Vernemq open-source brokers by trying to connect at least 50,000 clients to them.
However, I am not able to create those many connections. My Ubuntu 18.04
instance (8 core CPU, 15 GB RAM) on the Google Cloud fails to make any more successful connection after around 300-400.
I have tried making the following changes:
ulimit -n 64500 (for allowing these many file descriptors since every socket connection would require a file descriptor)
Please help me in making over 50,000 connections. Should I run 'n' number of threads and run the total_clients/total_threads clients under loop on each thread?
or should I create one thread for every client connection?
What should I do?
The following message appear on the "$SYS/#" topic once the clients start getting disconnected even though there is no disconnect data packet being sent by the client side.
$SYS/brokers/emqx#127.0.0.1/clients/112/disconnected {"clientid":"112","username":"undefined","reason":"closed","ts":1536587647}
import paho.mqtt.client as mqtt
from threading import Lock
import time
print_lock = Lock()
def s_print(str):
print_lock.acquire()
print(str)
print_lock.release()
def on_connect(client, userdata, flags, rc):
client_id = userdata["client_id"]
if (rc == 0):
s_print("connected " + client_id)
client.subscribe(client_id, 2)
def on_disconnect(client, userdata, rc):
client_id = userdata["client_id"]
s_print("disconnected: " + client_id + " reason " + str(rc))
def on_message(client, userdata, message):
topic = message.topic
payload = str(message.payload)
s_print("Recieved " + payload + " on " + topic)
if __name__ == '__main__':
n_clients = int(input("Enter no. of clients: "))
for i in range(n_clients):
client_id = str(i)
s_print(client_id)
userdata = {
"client_id" : client_id
}
client = mqtt.Client(client_id=client_id, clean_session=True, userdata=userdata)
client.on_connect = on_connect
client.on_disconnect =on_disconnect
client.on_message = on_message
client.connect("35.228.57.228", 1883, 60)
client.loop_start()
time.sleep(0.5)
while(1):
time.sleep(5)