Python UDP throughput is much slower than TCP throughput - python

I made two applications ('Client' and 'Server') written in Python.
On each of them beyond main thread I created two additonal threads, which are handling operations of sending / receiving data from each TCP / UDP parts.
So for client I have written code for:
clientTCP part:
class ClientTCP:
def __init__(self, host_address: str, port: int):
self.client_socket = None
self.host_address = host_address
self.port = port
def connect_to_server(self, is_nagle_disabled: bool):
try:
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if is_nagle_disabled:
self.client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
self.client_socket.connect((self.host_address, self.port))
except socket.error:
print('Error: probably wrong port passed for connection')
sys.exit(1)
def send_message_to_server(self, user_input: str):
try:
#print(f'MESSAGE TO SEND AS CLIENT TCP: {user_input}')
self.client_socket.sendall(bytes(user_input, 'utf-8'))
except socket.error as e:
print('OVER HERE SEND MESSAGE TCP ERROR')
print(f'Error: {e}')
self.client_socket.close()
sys.exit(1)
def receive_message_from_server(self, buffer_size: int):
data_from_server = []
while True:
byte_portion_of_data = self.client_socket.recv(buffer_size)
if byte_portion_of_data.decode("utf-8") in ['BUSY', 'READY']:
return data_from_server
elif byte_portion_of_data:
#print(f'TCP -> byte portion of data: {byte_portion_of_data.decode("utf-8")}')
data_from_server.append(byte_portion_of_data)
else:
print('Entirety of message from server received')
break
return data_from_server
def get_client_socket(self):
return self.client_socket
def __del__(self):
self.client_socket.close()
def tcp_send_data_to_server(client: ClientTCP, data_to_send: list[int], size_of_buffer: int, is_nagle_disabled: bool, stop):
try:
#print('OVER HERE TCP!!!')
client.connect_to_server(is_nagle_disabled)
client_connection_list = client.receive_message_from_server(10)
client_connection_message = ''.join([x.decode("utf-8") for x in client_connection_list])
if client_connection_message == 'BUSY':
return
elif client_connection_message == 'READY':
client.send_message_to_server(f"SIZE:{str(size_of_buffer)}")
while True:
#print("TCP!!!")
if stop():
break
message_to_send = ''.join([str(x) for x in data_to_send])
client.send_message_to_server(message_to_send)
time.sleep(1)
except ConnectionResetError:
print("Socket was closed due to some unknown reasons. Sorry. :(")
senderUDP
class SenderUDP:
def __init__(self, host_address: str, port: int):
self.client_socket = None
self.host_address = host_address
self.port = port
def connect_to_server(self):
try:
self.client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
except socket.error as e:
print(str(e))
sys.exit(1)
def send_message_to_server(self, input_to_send: str):
try:
#print(input_to_send)
#print('OVER HERE 1 UDP send')
self.client_socket.sendto(input_to_send.encode(), (self.host_address, self.port))
#print('OVER HERE 2 UDP send')
except Exception as e:
print('Error: ' + str(e))
self.client_socket.close()
sys.exit(1)
def get_client_socket(self):
return self.client_socket
def __del__(self):
self.client_socket.close()
def sending(sender: SenderUDP, data_to_send: list[int], size_of_buffer: int, stop):
try:
sender.connect_to_server()
sender.send_message_to_server(f"SIZE:{size_of_buffer}")
while True:
#print("UDP!!!")
if stop():
sender.send_message_to_server('END')
break
message_to_send = ''.join([str(x) for x in data_to_send])
sender.send_message_to_server(message_to_send)
sleep(1)
except ConnectionResetError:
print("Socket was closed due to some unknown reasons. Sorry. :(")
As for the 'Server' part:
serverTCP
import socket
import sys
import re
import time
from datetime import datetime
class ServerTCP:
def __init__(self, address, port, buffer: int):
self.server_socket = None
self.host_address = address
self.port = port
self.number_of_clients = 0
self.buffer = buffer
self.client_socket = None
self.count_bytes_from_client = 0
self.count_time_in_seconds = 0
def create_socket(self):
try:
socket.inet_aton(self.host_address)
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind((self.host_address, self.port))
except socket.error:
print('Error: ' + str(socket.error))
sys.exit(1)
def start_listening(self):
try:
self.server_socket.listen(1)
except socket.error as msg:
print('Error: ' + str(socket.error))
self.server_socket.close()
sys.exit(1)
def set_client_socket(self, client_socket):
self.client_socket = client_socket
self.count_bytes_from_client = 0
self.count_time_in_seconds = 0
self.client_socket.send('READY'.encode())
def handle_another_client(self, another_client_socket):
another_client_socket.send('BUSY'.encode())
another_client_socket.close()
def remove_client_socket(self):
self.client_socket.shutdown(socket.SHUT_WR)
self.client_socket.close()
self.buffer = 100
self.client_socket = None
def get_server_socket(self):
return self.server_socket
def get_client_socket(self):
return self.client_socket
def get_transmission_data(self):
return self.count_bytes_from_client / 1000, self.count_time_in_seconds
def __del__(self):
self.server_socket.shutdown(socket.SHUT_RDWR)
self.server_socket.close()
print("Server socket closed")
def __str__(self):
kbytes, time = self.get_transmission_data()
return f"TCP: {kbytes} per {time} seconds"
def tcp_get_data_from_client(server: ServerTCP):
# data_from_client = []
try:
start = datetime.now()
while True:
byte_portion_of_data = server.client_socket.recv(server.buffer)
if not byte_portion_of_data:
break
# data_from_client.append(byte_portion_of_data)
string_data_from_client = byte_portion_of_data.decode('utf-8')
if string_data_from_client.startswith('SIZE:'):
temp = re.findall(r'\d+', string_data_from_client[5:])
res = list(map(int, temp))
if len(res) != 0:
server.buffer = res[0]
start = datetime.now()
else:
server.count_bytes_from_client += len(string_data_from_client)
#print('End of the ServerTCP loop')
time.sleep(0)
end = datetime.now()
server.count_time_in_seconds += (end - start).total_seconds()
print(server)
server.remove_client_socket()
except ConnectionResetError:
print("Socket was closed due to some unknown reasons. Sorry. :(")
receiverUDP
import socket
import struct
import sys
import re
import time
from datetime import datetime
class ReceiverUDP:
def __init__(self, group, port: int, buffer: int):
self.group = group
self.port = port
self.socket = None
self.buffer = buffer
self.count_bytes_from_client = 0
self.count_time_in_seconds = 0
self.start = None
def starting(self):
try:
self.socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
self.socket.bind((self.group, self.port))
except socket.error as e:
print(f'Error: {str(e)}')
def get_transmission_data(self):
return self.count_bytes_from_client / 1000, self.count_time_in_seconds
def clean_transmission_data(self):
self.count_bytes_from_client = 0
self.count_time_in_seconds = 0
def close_of_receiver(self):
try:
self.socket.close()
except socket.error as e:
print(f'Error: {str(e)}')
def __str__(self):
kbytes, time = self.get_transmission_data()
return f"UDP: {kbytes} per {time}"
def receiving(receiver: ReceiverUDP, stop_thread):
is_first_client = True
try:
#print('Start of ReceiverUDP')
while True:
#print('Before recvfrom, ReceiverUDP')
#print(f'Start buffer: {receiver.buffer}')
data = receiver.socket.recvfrom(receiver.buffer)
message = data[0].decode("utf-8")
print(f"Server UDP, message: {message}")
if message.startswith("SIZE:"):
temp = re.findall(r'\d+', message[5:])
res = list(map(int, temp))
print(f'New buffer: {res[0]}')
if len(res) != 0:
if is_first_client:
receiver.start = datetime.now()
is_first_client = False
receiver.buffer = res[0]
print(f'New buffer assigned: {receiver.buffer}')
elif message.__contains__("END"):
receiver.count_time_in_seconds += (datetime.now() - receiver.start).total_seconds()
receiver.start = datetime.now()
print(receiver)
else:
receiver.count_bytes_from_client += len(message)
#print(f"Message from Server: ")
time.sleep(0)
except ConnectionResetError:
print(receiver)
print("Socket was closed due to some unknown reasons. Sorry. :(")
Static methods are of course 'thread' methods also.
Now back into the problem. I read on the internet that UDP transmission should be much faster than TCP. But that's not the case, in fact it's complete opposite for me.
When I put Server part on the container and run it + launched client with 'typed host_address' of docker gateway (it's something like 172.16.0.1') I got the same thing as earlier on running both on my machine.
On server application output I got such statistics for both TCP and UDP:
TCP: 2.3kB per 15.004 sec
UDP: 1.5kB per 15.009 sec
So clearly even now UDP is much slower than TCP. Why is that and what I did wrong?
I would be grateful for all advices.

Python UDP throughput is much slower than TCP throughput
This is to be expected. TCP is optimized for low overhead and will combine multiple send into as few packets on the wire as possible. With UDP instead each send will result in a single packet with all the overhead.
This overhead is especially noticable if the datagrams (i.e. the payload of send) are significantly smaller than the MTU of the link. And from a short look at your code it looks like that your are sending several small datagrams.
Apart from that it looks like you assume that a single send in the sender will match a single recv in the recipient. This is not true for TCP since data can be combined to reduce overhead: TCP is not a message protocol but an unstructured byte stream. The assumption is more or less true with UDP, but contrary to TCP there might be packet loss, packet reordering and duplication which you currently don't account for.

Related

Python Socket with Multiprocessing and Pickle issue

I am having a Pickle issue with SSL client to server communication using multiprocessing.
I have an SSL client that connects to the server:
SSLClient.py
import socket
import struct
import ssl
import copyreg
from os import path
import socket
import os
from pathlib import Path
from loguru import logger as log
from utils.misc import read_py_config
from datetime import datetime
from cryptography.fernet import Fernet
fernetkey = '1234567'
fernet = Fernet(fernetkey)
class SSLclient:
license = None
licenseencrypted = None
uuid = None
def __init__(self):
try:
path = Path(__file__).parent / "/lcl" #get unique license key
with path.open() as file:
self.licenseencrypted = file.read().rstrip()
self.license = fernet.decrypt(str.encode(self.licenseencrypted)).decode('ascii')
self.host, self.port = "127.0.0.1", 65416
except Exception as e:
log.error("Could not decode license key")
def connect(self):
self.client_crt = os.path.join(os.path.dirname(__file__), 'key/c-.crt')
self.client_key = os.path.join(os.path.dirname(__file__), 'key/ck-.key')
self.server_crt = os.path.join(os.path.dirname(__file__), 'key/s-.crt')
self.sni_hostname = "example.com"
self._context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=self.server_crt)
self._context.load_cert_chain(certfile=self.client_crt, keyfile=self.client_key)
self._sock = None
self._ssock = None
## ---- Client Communication Setup ----
HOST = self.host # The server's hostname or IP address
PORT = self.port # The port used by the server
try:
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._ssock = self._context.wrap_socket(self._sock, server_side=False, server_hostname=self.sni_hostname)
self._ssock.connect((HOST, PORT))
log.info("Socket successfully created")
except socket.error as err:
log.error("socket creation failed with error %s" %(err))
return False
log.info('Waiting for connection')
return True
def closesockconnection(self):
self._ssock.close()
def checkvalidsite(self):
#check if site is active
jsonobj = {
"uuid": self.license,
"ipaddress" : self.external_ip,
"req": "checkvalidsite"
}
send_msg(self._ssock, json.dumps(jsonobj).encode('utf-8'))
active = False
while True:
Response = recv_msg(self._ssock)
if not Response:
return False
if Response is not None:
Response = Response.decode('utf-8')
Response = json.loads(Response)
req = Response['req']
if req == "checkvalidsite":
active = Response['active']
self.info1 = Response['info1']
self.info2 = Response['info2']
return active
# ---- To Avoid Message Boundary Problem on top of TCP protocol ----
def send_msg(sock: socket, msg): # ---- Use this to send
try:
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
except Exception as e:
log.error("Sending message " + str(e))
def recv_msg(sock: socket): # ---- Use this to receive
try:
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
except Exception as e:
log.error("Receiving message " + str(e))
return False
def recvall(sock: socket, n: int):
try:
# Helper function to receive n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
except Exception as e:
log.error("Receiving all message " + str(e))
raise Exception(e)
I then have a server that is Multithreaded and accepts the connection and communicates with the client.
Server.py
import socket
import os
from socket import AF_INET, SOCK_STREAM, SO_REUSEADDR, SOL_SOCKET, SHUT_RDWR
import ssl
from os import path
from _thread import *
import struct # Here to convert Python data types into byte streams (in string) and back
import traceback
from threading import Thread
import json
import mysql.connector as mysql
import time
from loguru import logger as log
import threading
from cryptography.fernet import Fernet
fernetkey = '12213423423'
fernet = Fernet(fernetkey)
threadLocal = threading.local()
# ---- To Avoid Message Boundary Problem on top of TCP protocol ----
def send_msg(sock: socket, msg): # ---- Use this to send
try:
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
except Exception as e:
log.error("Error send_msg " + str(e))
def recv_msg(sock: socket): # ---- Use this to receive
try:
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
except Exception as e:
log.error("Receiving message " + str(e))
return False
def recvall(sock: socket, n: int):
try:
# Helper function to receive n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
except Exception as e:
log.error("Receiving all message " + str(e))
raise Exception(e)
# ---- Server Communication Setup
class Newclient:
def __init__(self):
self.addr = None
self.conn = None
self.uuid = None
class Server:
def __init__(self):
self.HOST = '127.0.0.1' # Standard loopback interface address (localhost)
self.PORT = 65416 # Port to listen on (non-privileged ports are > 1023)
self.ThreadCount = 0
self.threads = []
self.sock = None
def checkvalidsite(self, uuid, ipaddress, cursor, db_connection):
sql = "select * from myexample where uuid ='" + uuid + "'"
cursor.execute(sql)
results = cursor.fetchall()
active = False
for row in results:
active = row["active"]
siteid = row["info1"]
clientid = row["info2"]
return active, siteid, clientid
def Serverthreaded_client(self, newclient):
conn = newclient.conn
try:
while True:
# data = conn.recv(2048) # receive message from client
data = recv_msg(conn)
uuid = None
ipaddress = None
req = None
if not data :
return False
if data is not None:
data = json.loads(data.decode('utf-8'))
uuid = data['uuid']
req = data['req']
if uuid is not None and req is not None:
newclient.uuid = uuid
cursor, db_connection = setupDBConnection()
if req == "checkvalidsite":
ipaddress = data['ipaddress']
active, info1, info2 = self.checkvalidsite(uuid, ipaddress, cursor, db_connection)
data = {
"req": "checkvalidsite",
"uuid": uuid,
"active": active,
"info1" : info1,
"info2" : info2
}
if not data:
break
# conn.sendall(str.encode(reply))
send_msg(conn, json.dumps(data).encode('utf-8'))
log.info("Server response sent")
#conn.close()
closeDBConnection(cursor, db_connection)
else:
#send no message
a=1
except Exception as e:
log.warning(str(e))
log.warning(traceback.format_exc())
finally:
log.info("UUID Closing connection")
conn.shutdown(socket.SHUT_RDWR)
conn.close()
#conn.close()
def Serverconnect(self):
try: # create socket
self.server_cert = path.join(path.dirname(__file__), "keys/server.crt")
self.server_key = path.join(path.dirname(__file__), "keys/server.key")
self.client_cert = path.join(path.dirname(__file__), "keys/client.crt")
self._context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
self._context.verify_mode = ssl.CERT_REQUIRED
###self._context.load_cert_chain(self.server_cert, self.server_key)
self._context.load_cert_chain(certfile=self.server_cert, keyfile=self.server_key)
###self._context.load_verify_locations(self.client_cert)
self._context.load_verify_locations(cafile=self.client_cert)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) ###<-- socket.socket() ???
log.info("Socket successfully created")
except socket.error as err:
log.warning("socket creation failed with error %s" %(err))
try: # bind socket to an address
self.sock.bind((self.HOST, self.PORT))
except socket.error as e:
log.warning(str(e))
log.info('Waiting for a Connection..')
self.sock.listen(3)
def Serverwaitforconnection(self):
while True:
Client, addr = self.sock.accept()
conn = self._context.wrap_socket(Client, server_side=True)
log.info('Connected to: ' + addr[0] + ':' + str(addr[1]))
log.info("SSL established. Peer: {}".format(conn.getpeercert()))
newclient = Newclient()
newclient.addr = addr
newclient.conn = conn
thread = Thread(target=self.Serverthreaded_client, args =(newclient, ))
thread.start()
self.threads.append(newclient)
self.ThreadCount += 1
log.info('Thread Number: ' + str(self.ThreadCount))
def startserver():
server = Server()
server.Serverconnect()
server.Serverwaitforconnection()
serverthread = Thread(target=startserver)
serverthread.daemon = False
serverthread.start()
The server accepts the connection with SSL then waits for a message. It investigates the message command, executes the respective function and returns the data from the database as a response (checkvalidsite in this example).
All good so far (as far as I can tell).
I also have the main program that calls the SSLClient and connects.
Main program
remoteclient = SSLclient()
successfulconnection = remoteclient.connect()
siteactive = remoteclient.checkvalidsite()
So far all is well. However I also have the main program reading in frames from multiple cameras. Can be 20 cameras for example. In order to do this I created multiprocessing to deal with the camera load. Each camera or two cameras per, are assigned to a processor (depending on the number of cores in the machine).
(code below has been stripped out to simplify reading)
x = range(3, 6)
for n in x:
processes = multiprocessing.Process(target=activateMainProgram, args=(queue1, queue2, queue3, queue4, remoteclient, ))
processes.start()
When I try pass the remoteclient (SSLClient) as an argument I get the error:
cannot pickle 'SSLContext' object
I then (after reading online) added the code to the SSLClient:
def save_sslcontext(obj):
return obj.__class__, (obj.protocol,)
copyreg.pickle(ssl.SSLContext, save_sslcontext)
but then I get the error:
cannot pickle 'SSLContext' object
There are 2 options I experimented with:
Trying to get the pickle working (which would be ideal) as the processes themselves each need to communicate with the server. So the processes need to call functions from the SSLClient file. But I cannot get over the pickle issue and can't find a solution online
I then placed the remoteclient = SSLClient code outside the main function. Hoping it would run first and then be accessible to the processes. This worked, however what I learnt was that when a process is called (as it does not share memory) it reprocesses the entire file. Meaning if I have 10 processes each with 2 cameras then I would have 10 connections to the server (1 per process). This means on the server side I would also have 10 threads running each connection. Though it works, it seems significantly inefficient.
Being a noob and self taught in Python I am not sure how to resolve the issue and after 3 days, I figured I would reach out for assistance. If I could get assistance with the pickle issue of the SSLClient then I will have one connection that is shared with all processes and 1 thread in the server to deal with them.
P.s. I have cobbled all of the code together myself and being new to Python if you see that I am totally going down the wrong, incorrect, non-professional track, feel free to yell.
Much appreciated.
Update:
If I change the SSLClient code to:
def save_sslcontext(obj):
return obj.__class__, (obj.protocol,)
copyreg.pickle(ssl.SSLContext, save_sslcontext)
Then I get the error:
[WinError 10038] An operation was attempted on something that is not a socket
Not sure what is better..

Chat server and chat client

This program has both the ChatServer class and ChatClient class in the same file, and should be called in the terminal by --name=server --port=8800 for the server and --name=client1 --port=8800 for the client. The problem comes from the client class not being able to complete a try:
When running the program with server name, it seems to work fine. When it runs with client name, I get the output Failed to connect to chat server # port 8800.
You can find where this except statement lies.
import select
import socket
import sys
import signal
import _pickle as cPickle
import struct
import argparse
SERVER_HOST = 'localhost'
CHAT_SERVER_NAME = 'server'
# Some utilities
def send(channel, *args):
buffer = cPickle.dumps(args)
value = socket.htonl(len(buffer))
size = struct.pack("L", value)
channel.send(size)
channel.send(buffer)
def receive(channel):
size = struct.calcsize("L")
size = channel.recv(size)
try:
size = socket.ntohl(struct.unpack("L", size)[0])
except struct.error as e:
return ''
buf = ""
while len(buf) < size:
buf = channel.recv(size - len(buf))
return cPickle.loads(buf)[0]
class ChatServer(object):
def __init__(self, port, backlog=5):
self.clients = 0
self.clientmap = {}
self.outputs = []
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Enable re-using socket address
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((SERVER_HOST, port))
print('Server listening to port: %s...' % port)
self.server.listen(backlog)
# Catch keyboard interrupts
signal.signal(signal.SIGINT, self.sighandler)
def sighandler(self, signum, frame):
# Close the server
print("Shutting down server...")
# Close existing client sockets
for output in self.outputs:
output.close()
self.server.close()
def get_client_name(self,client):
info = self.clientmap[client]
host, name = info[0][0], info[1]
return '#'.join((name, host))
def run(self):
inputs = [self.server, sys.stdin]
self.outputs = []
running = True
while running:
try:
readable, writeable, exceptional = select.select(inputs, self.outputs, [])
except select.error as e:
break
for sock in readable:
if sock == self.server:
# handle the server socket
client, address = self.server.accept()
print("Chat Server: got connection %d from %s" % (client.fileno(), address))
# Read the login name
cname = receive(client).split('NAME: ')[1]
# Compute client name ad send back
self.clients += 1
send(client, 'CLIENT: ' + str(address[0]))
inputs.append(client)
self.clientmap[client] = (address, cname)
# Send joining information to other clients
msg = "\n(Connected: New client (%d) from %s)" % (self.clients, self.get_client_name(client))
for output in self.outputs:
send(output, msg)
self.outputs.append(client)
elif sock == sys.stdin:
# Handle standard input
junk = sys.stdin.readline()
running = False
else:
# Handle all other sockets
try:
data = receive(sock)
if data:
# Send as new client's message..
msg = '\n[' + self.get_client_name(sock) + ']>>' + data
# Send data to all except ourself
for output in self.outputs:
if output != sock:
send(output, msg)
else:
print("Chat server: %d hung up" % sock.fileno())
self.clients -= 1
sock.close()
inputs.remove(sock)
self.outputs.remove(sock)
# Sending client leaving info to others
msg = "\n(Now hung up: Client from %s" % self.get_client_name(sock)
for output in self.outputs:
send(output, msg)
except socket.error as e:
# Remove
inputs.remove(sock)
self.outputs.remove(sock)
self.server.close()
class ChatClient(object):
def __init__(self, name, port, host=SERVER_HOST):
self.name = name
self.connected = False
self.host = host
self.port = port
# Initial Prompt
self.prompt = '[' + '#'.join((name, socket.gethostname().split('.')[0])) + ']> '
# Connect to server at port
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, self.port))
print("Now connected to chat server# port %d" % self.port)
self.connected = True
# Send by name...
send(self.sock, 'NAME: ' + self.name)
data = receive(self.sock)
# Contains client address, set it
addr = data.split('CLIENT: ')[1]
self.prompt = '[' + '#'.join((self.name, addr)) + ']>'
except socket.error as e:
print("Failed to connect to chat server # port %d" % self.port)
sys.exit(1)
def run(self):
while self.connected:
try:
sys.stdout.write(self.prompt)
sys.stdout.flush()
# Wait for input from stdin and socket
readable, writable, exceptional = select.select([0, self.sock], [], [])
for sock in readable:
if sock == 0:
data = sys.stdin.readline().strip()
if data:
send(self.sock, data)
elif sock == self.sock:
data = receive(self.sock)
if not data:
print('Client shutting down')
self.connected = False
break
else:
sys.stdout.write(data + '\n')
sys.stdout.flush()
except KeyboardInterrupt:
print("Client interrupted")
self.sock.close()
break
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Socket Server Example with Select')
parser.add_argument('--name', action="store", dest="name", required=True)
parser.add_argument('--port', action="store", dest="port", type=int, required=True)
given_args = parser.parse_args()
port = given_args.port
name = given_args.name
if name == CHAT_SERVER_NAME:
server = ChatServer(port)
server.run()
else:
client = ChatClient(name=name, port=port)
client.run()

Sockets Programming with Video Streaming with python

I have to make a project where i accept multiple client connections and I have to send screenshots of the screen of the client to the server and show it on the screen and if I have to be able to stream clients screen to the server. How do I go about this? I've been trying to make this for about 2 months now. I am stuck and don't know how to make it.
Any reply is greatly appreciated.
This is the server code I have so far.
import threading
import socket
from queue import Queue
NUMBER_OF_THREADS = 2
JOB_NUMBER = [1,2]
queue = Queue()
class MultiServer(object):
#Initialize Server Object with host, port, socket, all connections and all the IP addresses
def __init__(self):
self.host = ""
self.port = 8965
self.socket = None
self.all_connections = []
self.all_addresses = []
self.all_users = []
#Create Socket
def socket_create(self):
#If socket creation fails except the error and show to the User and exit the program
try:
self.socket = socket.socket()
except socket.error as msg:
print("Socket creation failed, error: " +str(msg))
sys.exit(1)
#Use socket.setsockopt to keep reusing the same port
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return
def socket_bind(self):
#Bind socket to port and wait for incoming connections
try:
self.socket.bind((self.host, self.port))
self.socket.listen(5)
#Is binding the socket fails show error to the user and try again in 5 seconds
except socket.error as msg:
print("Socket binding error: " + str(msg))
time.sleep(5)
self.socket_bind()
return
def accept_connections(self):
#Accept Connections from multiple clients and save to list
#Close all old connections to prevent errors
for c in self.all_connections:
c.close()
self.all_connections = []
self.all_addresses = []
while True:
try:
conn, address = self.socket.accept()
conn.setblocking(1)
client_hostname = conn.recv(1024).decode("utf-8")
address= address + (client_hostname,)
except Exception as ex:
print('Error accepting connections: %s' %str(ex))
# Inifinite Loop
continue
self.all_connections.append(conn)
self.all_addresses.append(address)
print('\nConnection has been estalished: {0} ({1})'.format(address[-1], address[0]))
return
def send_commands(self):
while True:
cmd = input("$:")
if cmd == "list":
self.list_connections()
def list_connections(self):
""" List all connections """
results = ''
for i, conn in enumerate(self.all_connections):
try:
conn.send(str.encode(' '))
conn.recv(20480)
except:
del self.all_connections[i]
del self.all_addresses[i]
continue
results += str(i) + ' ' + str(self.all_addresses[i][0]) + ' ' + str(
self.all_addresses[i][1]) + ' ' + str(self.all_addresses[i][2]) + '\n'
print('----- Clients -----' + '\n' + results)
return
def create_workers():
server = MultiServer()
for i in range(2):
t = threading.Thread(target=work, args=(server,))
t.daemon = True
t.start()
def work(server):
while True:
x = queue.get()
if x == 1:
server.socket_create()
server.socket_bind()
server.accept_connections()
if x == 2:
server.send_commands()
queue.task_done()
return
def create_jobs():
for x in JOB_NUMBER:
queue.put(x)
queue.join()
def main():
create_workers()
create_jobs()
if __name__ == '__main__':
main()
And this is the Client code I have so far
import socket
class Client(object):
def __init__(self):
self.serverHost = '127.0.0.1'
self.serverPort = 8965
self.socket = None
def socket_create(self):
""" Create a socket """
try:
self.socket = socket.socket()
except socket.error as e:
print("Socket creation error" + str(e))
return
return
def socket_connect(self):
""" Connect to a remote socket """
try:
self.socket.connect((self.serverHost, self.serverPort))
except socket.error as e:
print("Socket connection error: " + str(e))
time.sleep(5)
raise
try:
self.socket.send(str.encode(socket.gethostname()))
except socket.error as e:
print("Cannot send hostname to server: " + str(e))
raise
return
def recieve_requests(self):
""" Controle of de server nog actief is """
try:
self.socket.recv(10)
except Exception as ex:
print("Couldn't recieve Commands: " + str(ex))
return
while True:
data = self.socket.recv(20480).decode("utf-8")
print(data)
print("mislukt")
client = Client()
client.socket_create()
def main():
client = Client()
client.socket_create()
try:
client.socket_connect()
except Exception as ex:
print(f"Socket connection error: {ex}")
while True:
try:
client.recieve_requests()
except Exception as ex:
print(ex)

Unable to find Ethernet or socket connection exist in python socket

i have a small client application which needs to maintain a persistent socket connection to the server.
The connection establishment seems to work fine. also when the server closes the socket i am able to detect.
But when the client Ethernet cable is unplugged .it never throws a exception when i try send or receive data in sock.
when i try to send i get send data success and when i try to receive i get receive timeout
what i am missing in my class.Please guide
Note : it almost took 14 mins for the socket to find disconnect
KEEP ALIVE DATA:
echo 30 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
echo 30 > /proc/sys/net/ipv4/tcp_keepalive_time
code implementation:
import logFormat as log
import socket
import traceback
import sys
import select
import time
import messageId as messasgeId # Name convention to be decided Many DB access done through this class
def synchronized(L):
def lock_around(f):
def locked(*a, **k):
with L:
return f(*a, **k)
locked.__name__ = f.__name__
locked.__doc__ = f.__doc__
return locked
return lock_around
class socketHandler(object):
__single = None # the one, true Singleton
def __new__(classtype, *args, **kwargs):
if classtype != type(classtype.__single):
classtype.__single = object.__new__(classtype, *args, **kwargs)
return classtype.__single
def __init__(self, name=None):
self.name = name
self.throttleCount = 100
self.loggerCount = 0
self.timeoutDefault=0.2
def socketSend(self, data):
try:
self.sock.send(data)
log.debugLog("Socket data send complete", True)
return True
except:
log.debugLog("Socket data send with error/timeout", True)
raise
def recieveProtoBufHeader(self, headerLength, timeout=10):
try:
self.sock.settimeout(timeout)
expHeaderBytes = ord(headerLength)
log.debugLog("Expected socket data length: %s with timeout: %s" % (expHeaderBytes, timeout), True)
HeaderData = self.sock.recv(expHeaderBytes)
log.debugLog("Data received in socket: %s" % HeaderData, True)
self.sock.settimeout(self.timeoutDefault)
return HeaderData
except:
log.debugLog("recieveProtoBufHeader with error/timeout", True)
raise
def recieveProtoBufData(self, dataSize, timeout=0.1):
try:
log.debugLog("Expected socket data length: %s with timeout: %s" % (dataSize, timeout), True)
self.sock.settimeout(timeout)
dataBuffer = self.sock.recv(dataSize)
log.debugLog("Data received in socket: %s" % dataBuffer, True)
self.sock.settimeout(self.timeoutDefault)
return dataBuffer
except:
log.debugLog("recieveProtoBufData with error/timeout", True)
raise
def recieveData(self, dataSize, timeout=0.1):
try:
self.sock.settimeout(timeout)
dataBuffer = self.sock.recv(1)
if len(dataBuffer) == dataSize:
log.debugLog("Data received in socket: %s" % dataBuffer.encode('hex'), True)
log.debugLog("Data received length: %s" % len(dataBuffer), True)
else:
self.sock.close()
log.debugLog("Server side socket closed raising socket error", True)
raise socket.error, "Socket error raised"
self.sock.settimeout(self.timeoutDefault)
return dataBuffer
except socket.timeout:
raise # Can add log foo socket timeout
except:
log.debugLog("Unknown exception occurred so closing socket ", True)
self.sock.close()
raise
def recieveDataCheck(self):
try:
self.sock.settimeout(0.2)
dataBuffer = self.sock.recv(1)
if len(dataBuffer) == 1:
log.debugLog("Data received in socket: %s" % dataBuffer.encode('hex'), True)
log.debugLog("Data received length: %s" % len(dataBuffer), True)
else:
self.sock.close()
log.debugLog("Server side socket closed raising socket error", True)
raise socket.error, "Socket error raised"
return dataBuffer
except socket.timeout:
raise # Can add log foo socket timeout
except:
log.debugLog("Unknown exception occurred so closing socket ", True)
self.sock.close()
raise
def closeSocket(self):
try:
self.sock.close()
self.sock = None
return True
except:
log.debugLog("Close socket with error", True)
raise
def recieveCheck(self):
try:
ready = select.select([self.sock], [], [], 0.2)
print ready
if len(ready[0]) == 0:
return True
else:
return False
except:
raise
def connectSocket(self, retryCount=5):
try:
self.loggerCount+=1
if self.loggerCount % self.throttleCount == 0:
throttleLog=True
else:
throttleLog=False
#host = "192.168.10.134"
host = messasgeId.getSwitchIP()
# host = "localhost"
#port = int(8001)
port = int(messasgeId.getSwitchPort())
log.debugLog("connection with switch server ip: %s port: %s on progress" % (host, port), True)
while retryCount:
self.sock = None
self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
#self.sock.bind(host,port)
self.sock.settimeout(0.1)
try:
self.sock.connect((host, port))
#assert(self.recieveCheck())
log.debugLog("Socket connection established successfully", True)
self.loggerCount = 0
return True
except:
log.debugLog("Socket Connection Retry Count Remaining Progress(%s)" % retryCount, True)
time.sleep(5)
retryCount -= 1
if retryCount == 0:
return False
return False
except:
log.debugLog("Unexpected error in socket connection", True)
raise

Control TCP server in Python

I use unittests to check correctness of TCP client communication, so I need some TCP server that I can control it and that it sends to client (and when).
A simple test should look like:
server = Server("localhost", 5555)
server.start()
client.connect()
self.assertTrue("login_message" in server.received_data)
server.send_to_client(reject_messages)
self.assertTrue("login_again" in server.received_data)
time.sleep(10)
self.assertTrue("login_again_and_again" in server.newest_received_data)
server.stop()
self.assertTrue("login failed" in client.logs)
I need the full flow control, what could be used for implementing Server?
Now I try to use threaded SocketServer, but I don't have an access neither to data, nor to controlling it..
I don't know if anybody needs this, but anyway:
I used gevent server
from gevent import sleep, socket
from gevent.server import StreamServer
class Connection:
def __init__(self, host, port):
self.server = StreamServer((host, port), self.handle)
self.data = []
self.socks = []
self.pointer = 0
def handle(self, sock, address):
self.socks.append(sock)
while True:
line = sock.recv(1024)
if line:
self.data += [line]
else:
break
sock.close()
self.socks.remove(sock)
def send(self, msg):
if self.socks:
sock2send = self.socks[-1]
try:
sock2send.send(msg)
except IOError, e:
print "Can't send message '%s'! Exception:" % msg, e
else:
print "No sockets to send the message to"
def start(self):
self.server.start()
def serve_forever(self):
self.server.serve_forever()
def close(self):
self.server.stop()
for sock in self.socks:
sock.close()
def new_data(self):
newest = self.data[self.pointer:]
self.pointer = len(self.data)
return newest
And then unittest looks like this:
def testTCPClient(self):
j = lambda x: "".join(x)
server = Connection("", 5555)
server.start()
client.run()
sleep(3)
data = j(server.new_data())
self.assertTrue("login" in data)
sleep(2)
server.send("login approve")
sleep(2)
data = j(server.new_data())
self.assertTrue("after_login" in data)
server.send("logout")
sleep(2)
data = j(server.new_data())
self.assertTrue("received_logout" in data)
server.close()
self.assertTrue("disconnected" in client.logs)

Categories

Resources