'Unable to get TCP client working in Flask APP
I have 2 programs running on a server with TCP service to communicate with the outside world. Now I would like to implement a FLASK App as an intermedium service to pass information between the outside users and the TCP sevices. How should I implement it?
workflow I have implemented the TCP server & client scripts (https://www.geeksforgeeks.org/simple-chat-room-using-python/), both work fine independently.
chat_server.py
#!/usr/bin/env python3
# chat_server.py localhost 6673
import socket, threading, sys
# Global variable that mantain client's connections
connections = []
def handle_user_connection(connection: socket.socket, address: str) -> None:
'''
Get user connection in order to keep receiving their messages and
sent to others users/connections.
'''
while True:
try:
# Get client message
msg = connection.recv(1024)
# If no message is received, there is a chance that connection has ended
# so in this case, we need to close connection and remove it from connections list.
if msg:
# Log message sent by user
print(f'{address[0]}:{address[1]} - {msg.decode()}')
# Build message format and broadcast to users connected on server
msg_to_send = f'From {address[0]}:{address[1]} - {msg.decode()}'
broadcast(msg_to_send, connection)
# Close connection if no message was sent
else:
remove_connection(connection)
break
except Exception as e:
print(f'Error to handle user connection: {e}')
remove_connection(connection)
break
def broadcast(message: str, connection: socket.socket) -> None:
'''
Broadcast message to all users connected to the server
'''
# Iterate on connections in order to send message to all client's connected
for client_conn in connections:
# Check if isn't the connection of who's send
if client_conn != connection:
try:
# Sending message to client connection
client_conn.send(message.encode())
# if it fails, there is a chance of socket has died
except Exception as e:
print('Error broadcasting message: {e}')
remove_connection(client_conn)
def remove_connection(conn: socket.socket) -> None:
'''
Remove specified connection from connections list
'''
# Check if connection exists on connections list
if conn in connections:
# Close socket connection and remove connection from connections list
conn.close()
connections.remove(conn)
def server() -> None:
'''
Main process that receive client's connections and start a new thread
to handle their messages
'''
try:
# Create server and specifying that it can only handle 4 connections by time!
socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_instance.bind((SERVER, PORT))
socket_instance.listen(4)
print('Server running!')
while True:
# Accept client connection
socket_connection, address = socket_instance.accept()
# Add client connection to connections list
connections.append(socket_connection)
# Start a new thread to handle client connection and receive it's messages
# in order to send to others connections
threading.Thread(target=handle_user_connection, args=[socket_connection, address]).start()
except Exception as e:
print(f'An error has occurred when instancing socket: {e}')
finally:
# In case of any problem we clean all connections and close the server connection
if len(connections) > 0:
for conn in connections:
remove_connection(conn)
socket_instance.close()
if __name__ == "__main__":
SERVER = sys.argv[1]
PORT = int(sys.argv[2])
server()
chat_client.py
#!/usr/bin/env python3
# chat_client.py localhost 6673
# Python program to implement client side of chat room.
import socket
import select
import sys
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if len(sys.argv) != 3:
print ("Correct usage: script, IP address, port number")
exit()
IP_address = str(sys.argv[1])
Port = int(sys.argv[2])
server.connect((IP_address, Port))
while True:
# maintains a list of possible input streams
sockets_list = [sys.stdin, server]
""" There are two possible input situations. Either the
user wants to give manual input to send to other people,
or the server is sending a message to be printed on the
screen. Select returns from sockets_list, the stream that
is reader for input. So for example, if the server wants
to send a message, then the if condition will hold true
below.If the user wants to send a message, the else
condition will evaluate as true"""
read_sockets,write_socket, error_socket = select.select(sockets_list,[],[])
for socks in read_sockets:
if socks == server:
message = socks.recv(2048)
print (f'Received from server: {message.decode()}\n')
else:
message = sys.stdin.readline()
server.send(message.encode())
sys.stdout.write("<You>")
sys.stdout.write(message)
sys.stdout.flush()
server.close()
FLASK app.py:
#!/usr/bin/env python3
# app.py localhost 6673
import flask
from flask import request, jsonify
import re
import socket
import time
import sys
import os
from signal import SIGKILL
from subprocess import Popen, PIPE
app = flask.Flask(__name__)
app.config["DEBUG"] = True
# =================================================================
# I need to send the data_array to TCP_CONN and wait the reply, then
# return the reply back to user through HTTP
# =================================================================
def send_to_tcp(server, port, data_array):
reply = {'fakeData': 123}
return reply
def close_port(port):
process = Popen(["lsof", "-i", ":{0}".format(port)], stdout=PIPE, stderr=PIPE)
stdout, stderr = process.communicate()
for process in str(stdout.decode("utf-8")).split("\n")[1:]:
data = [x for x in process.split(" ") if x != '']
if (len(data) <= 1):
continue
os.kill(int(data[1]), SIGKILL)
@app.route('/', methods=['GET'])
def home():
return '''<h1>Testing</h1>
<p>Testing.</p>'''
@app.route('/all', methods=['GET'])
def api_all():
return jsonify({'a':1, 'b':2})
@app.route('/item', methods=['GET'])
def api_id():
if 'params' in request.args:
#print(request.args)
#id = int(request.args['id'])
params = re.findall(r'<([^<>]+)>', request.args['params'])
returns = send_to_tcp(HOST, PORT, params)
for p in params:
print(p)
return jsonify(params)
else:
return "Error: No params field provided."
if __name__ == '__main__':
HOST = sys.argv[1]
PORT = int(sys.argv[2])
TCP_CONN = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
TCP_CONN.connect((HOST, PORT))
try:
app.run(host=HOST, port=5000, debug=True)
#from waitress import serve
#serve(app, host=HOST, port=PORT)
#threading.Thread(target=lambda: serve(app, host=HOST, port=5000)).start()
#threading.Thread(target=lambda: app.run(host=HOST, port=5000, debug=True, use_reloader=False)).start()
finally:
close_port(PORT)
The FLASK app works fine as well with the fake send_to_tcp() function:
http://localhost:5000/item?params=%3Caaa:111%3E%3Cbbb:222%3E
FLASK's return
But it failed to talk to the TCP services. I tried something below but it didn't work:
def send_to_tcp(server, port, data_array):
s = TCP_CONN
r = ''
while True:
for d in data_array:
ds = d
s.send(ds.encode())
print(ds)
data = s.recv(4096)
if len(data) == 0:
break
print(f"Received {data!r}")
r = f'{r}{data.decode()}'
return r
Could someone please help? Thanks in advance!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
