How to send message sequences over a socket? - python-3.x

I'm experimenting with communication via sockets in Python. I have a server.py-file and a client.py. The latter is asking for a user input and send this to the server. Depending on the user input, the server is playing 2 different sound. This works a single time but not for a second attempt.
I've put in some print(XYZ) lines to see where the programm is stopping. After sending the first message the "print("...")"-line of my server.py is no longer executed. So it looks like server_socket.accept() is no longer successful. Why is that?
This is server.py:
import socket
import winsound
fnameBeep = "beep.wav"
fnameBop = "bop.wav"
server_socket = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
server_addr = ("127.0.0.1", 1337)
server_socket.bind(server_addr)
server_socket.listen(1000)
while True:
print("waiting...")
(client_socket, addr) = server_socket.accept()
print("...")
msg = client_socket.recv(1024)
print(msg)
print("Received: " + str(msg, "utf8"))
if str(msg, "utf8") == '1':
winsound.PlaySound(fnameBeep, winsound.SND_FILENAME)
else:
winsound.PlaySound(fnameBop, winsound.SND_FILENAME)
This is client.py:
import socket
client_socket = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
server_addr = ("127.0.0.1", 1337)
print("Beep = 1; Bop = 2")
client_socket.connect(server_addr)
while True:
print('waiting ...')
UserInput = input('Your choice: ')
print('Sending: ' + UserInput)
client_socket.send(bytes(UserInput, "utf8"))
print('Sent!')
What is going wrong? the general communicaiton is working but I'd like to send a sequence of messages to the server.

The first thing you'll want to do is move your accept() call up to above the top of your while loop -- as it is, your code is trying to accept a new TCP connection after each command, which I think it not what you wanted to do -- presumably you want to keep a single TCP connection open and receive multiple commands over that TCP connection instead.
The second issue is framing: the thing to remember about TCP is that it implements a raw stream of bytes, and doesn't do any fixed framing of messages on its own -- e.g. if you execute the following calls on the sending side:
client_socket.send("123")
client_socket.send("456")
client_socket.send("789")
... the on the receiving side, the server might see the following data-chunks returned from its subsequent recv() calls:
recv() call #1: 12345
recv() call #2: 67
recv() call #3: 89
... or, it might instead (depending on how the network is working, phase of the moon, etc) get the following:
recv() call #1: 1
recv() call #2: 2345678
recv() call #3: 9
or it might get all the sent data in a single call:
recv() call #1: 123456789
or it might even receive each byte via a separate call:
recv() call #1: 1
recv() call #2: 2
recv() call #3: 3
recv() call #4: 4
recv() call #5: 5
recv() call #6: 6
recv() call #7: 7
recv() call #8: 8
recv() call #9: 9
... or any other combination you can imagine.
So the question is, given the uncertainty of the data-chunking, how can your receiver know that your client intended to send ("file_name1.wav" and then "file_name2.wav") rather than just ("file_name1.wavfile_name.wav"), or ("file", "name1.wavfile", "name.wav"), or etc?
In order to parse the incoming TCP bytes unambiguously, the receiver has to know how they are framed. For a simple program like yours, the framing logic could be straightforward -- e.g. just declare a rule that each string will have a newline character at the end. Then your server can simply keep receiving bytes (and adding them to the end of a string) until it sees a newline byte, at which point it knows it has received a full user-command, so it can at that point handle that command, then remove all of the string (up to and including the newline character) before continuing its parsing for the next string.
Another way to do it is to have the sender include a short (fixed-length) header before each command indicating the number of bytes that the receiver should expect to see in the next command. Then the server can read the header (because the header is fixed-length, the server will know how many bytes it needs to read before it has the full header to look at), and then read that-many bytes, then handle the command, then repeat.

Related

Multi-client TCP socket server (server sends query upon connection then waits for client response + polls client)

I modified the code above from what Torxed posted in the URL: Sending string via socket (python)
I used this as a base as want a multi-threading python3 server. I'm still learning Python so am getting a little stuck.
I've got 2 threads working where the 1st (a background) thread sends a UDP broadcast to inform clients that the source host (the python server) is waiting for client connections.
The client detects the UDP broadcast (on port 1300) & initiates a new TCP session to port 1200. I have got the python server to immediately send some bytes (bytes as not ascii) to the client which 'asks' the client to identity itself upon connection (the client does not initiate the conversation other than establishing the TCP session). In this case the client host is a solar inverter.
The client responds with some basics such as it's serial number, software version, etc which I want to store. Currently I'm not able to store the reply in a variable as it's byte sequence, not ascii so I'm currently writing the datagram to a local file until I figure out how to unpack the byte sequence & save in variables associated to the client session. This will be used to aid identifying which data-set is associated to which client.
Every 15 seconds after that, I want the python server to send some more bytes (again bytes not ascii) which will inform the client to reply with some PV (solar) data.
The client will reply with the PV data which I want to store associated to the client session.
Lastly if an event occurs on the client (inverter such as AC loss power) it will send to the server another datagram in the established TCP session a specifically formatted byte sequence so the server needs to handle an independent response from the client which will be to initiate sending an email (to me) containing the event type. Is it better to use a single recv datagram handler with something like a case statement to identify the response then process it accordingly (but the server must first receive the client's id).
I want the TCP session to remain open indefinitely but also support a 2nd inverter to connect too & allow them should connectivity is temporally interpreted to re-connection without issue.
Any help appreciated as I'm stuck with being unable to read the response from the client.
Here is what I currently have:
#!/usr/bin/env python3
# need threading support to run broadcast UDP 1300 thread
from threading import *
from socket import *
# import time for sleep/delay timers
import time
# Now we can create socket object
serversocket = socket(AF_INET, SOCK_STREAM)
#serversocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
#serversocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
# Set the IP address and port to listen on
HOST = "192.168.1.11"
PORT = 1200
# Bind the socket on the interface/port
serversocket.bind((HOST, PORT))
# This is the client thread allowing multiple clients to connect
class client(Thread):
# This section is what will run in the background
def background():
while True:
cs.sendto(b'\x55\xaa\x00\x40\x02\x00\x0b\x49\x20\x41\x4d\x20\x53\x45\x52\x56\x45\x52\x04\x3a', ("192.168.1.255", 1301))
time.sleep(5)
# This section is what will run in the foreground
def __init__(self, socket, address):
Thread.__init__(self)
self.sock = socket
self.addr = address
self.start()
def run(self):
while True:
msg = (b'\x55\xaa\x01\x03\x02\x00\x00\x01\x05')
self.sock.send(msg)
#Open recv.txt file in write mode
recvdatafile1 = open("recv1.txt", "wb")
print("Destination file name will be recv1.txt on the server\n")
# Receive intiate data from client side
# RecvData = self.sock.recv(1024)
while True:
recvdatafile1.write(RecvData)
# recvdatafile1.write(self.sock.recv(1024))
RecvData = self.sock.recv(1024)
# Request initial configuration information (replies with S/N, model number, etc
#msg = (b'\x55\xaa\x01\x03\x02\x00\x00\x01\x05')
#conn.send(msg)
# Close the file opened at server side once copy is completed
recvdatafile1.close()
print("\n File has been copied successfully \n")
#Open recv.txt file in write mode
recvdatafile2 = open("recv2.txt", "wb")
print("Destination file name will be recv2.txt on the server\n")
# Request PV data
msg = (b'\x55\xaa\x01\x02\x02\x00\x00\x01\x04')
conn.send(msg)
# Receive any data from client side
RecvData = conn.recv(1024)
while RecvData:
recvdatafile2.write(RecvData)
RecvData = conn.recv(1024)
# Close the file opened at server side once copy is completed
recvdatafile2.close()
print("\n File has been copied successfully \n")
# Close connection with client
conn.close()
print("\n Server closed the connection \n")
time.sleep(3)
# Come out from the infinite while loop as the file has been copied from client.
#break
serversocket.listen(5)
print("Server is listing on port:", PORT, "\n")
while 1:
clientsocket, address = serversocket.accept()
client(clientsocket, address)
print ("connection found!")
data = clientsocket.recv(1024).decode()
print (data)
b = threading.Thread(name='background', target=background)
f = threading.Thread(name='foreground', target=foreground)
b.start()
f.start()
Server Output:
user#server:~# python3 ./Samil_Solar_River_Monitor_Server.py
Server is listing on port: 1200
connection found!
Destination file name will be recv1.txt on the server
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "./Samil_Solar_River_Monitor_Server.py", line 62, in run
recvdatafile1.write(RecvData)
UnboundLocalError: local variable 'RecvData' referenced before assignment
Traceback (most recent call last):
File "./Samil_Solar_River_Monitor_Server.py", line 109, in <module>
data = clientsocket.recv(1024).decode()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xaa in position 1: invalid start byte
user#server:~#
Client Output:
user#client:~$ python3 test-solar-client.py 192.168.1.11
################## Below rcved from srv ##################
b'U\xaa\x01\x03\x02\x00\x00\x01\x05' <-- This is good/correct
################## Below rcved from srv ##################
b'' <-- this & below appears when traceback on server occurs
Traceback (most recent call last):
File "test-solar-client.py", line 47, in <module>
s.send(SendData)
BrokenPipeError: [Errno 32] Broken pipe
user#client:~$

My if & elif not working on sockets (python 3)

I tried to create a server that receives commands from the client
And to identify which command the client wrote I used if & elif
But when I run the program and write a command from the client, only the first command works (the command on the if) and if I try another command (from elif & else)
The system just doesn't respond (like she's waiting for something)
The Server Code:
import socket
import time
import random as rd
soc = socket.socket()
soc.bind(("127.0.0.1", 7777))
soc.listen(5)
(client_socket, address) = soc.accept()
if(client_socket.recv(4) == b"TIME"):
client_socket.send(time.ctime().encode())
elif(client_socket.recv(4) == b"NAME"):
client_socket.send(b"My name is Test Server!")
elif(client_socket.recv(4) == b"RAND"):
client_socket.send(str(rd.randint(1,10)).encode())
elif(client_socket.recv(4) == b"EXIT"):
client_socket.close()
else:
client_socket.send(b"I don't know what your command means")
soc.close()
The Client Code:
import socket
soc = socket.socket()
soc.connect(("127.0.0.1", 7777))
client_command_to_the_server = input("""
These are the options you can request from the server:
TIME --> Get the current time
NAME --> Get the sevrer name
RAND --> Get a Random int
EXIT --> Stop the connect with the server
""").encode()
soc.send(client_command_to_the_server)
print(soc.recv(1024))
soc.close()
if(client_socket.recv(4) == b"TIME"):
client_socket.send(time.ctime().encode())
This will check the first 4 byte received from the server
elif(client_socket.recv(4) == b"NAME"):
client_socket.send(b"My name is Test Server!")
This will check the next 4 bytes received from the server. Contrary to what you assume it will not check the first bytes again since you called recv to read more bytes. If there are no more bytes (likely, since the first 4 bytes are already read) it will simply wait. Instead of calling recv for each comparison you should call recv once and then compare the result against the various strings.
Apart from that: recv will only return up to the given number of bytes. It might also return less.

Read and write to UART pyserial using timeout functionality

I have written a UART py-serial script, which looks like.
import serial
import time
try:
ser = serial.Serial(
#port='/dev/ttyAMA0',
port='/dev/ttyUSB1',
baudrate = 9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=2
)
print ('Connection is open : ',str(ser.isOpen()))
except Exception as e:
print ("Something got wrong: ", e)
ser.write(b'hello..')
and have a similar script as receiver.
what I want is:
suppose timeout = 5
ser.write(b'Helloo...')
ser.flush()
ser.readline()
the other script is reading it and sending a response via serial only.
this readline should wait maximum for this timeout, if it received response in 2 seconds then it should not wait for 5seconds.
But using this timeout it waits for 2 seconds, even if the data is received in a second.
so my concern is to wait maximum for timeout, but if data received earlier it should end and proceed.
Not able to find this functionality, please help.
found a way for this to work:
pyserial have this inbuit functionality, for eg:
ser.read_until(b'#') #just specify EOL here
It will break if this (#) is found. if not, will wait for timeout and return at end.
You can also use seperate timeouts for read and write:
timeout=5 #read timeout
write_timeout=5 # write timeout

Python36 and sockets

So I'm connected to an IRC chat using socket.connec
I log in by passing my variables via socket.send
The log in is successful and then I sit in a while true loop using
Socket.recv(1024)
If I just continually print the response everything looks fine, but let's say I want to add to the end of the string... I noticed that socket.recv doesn't always get the full message (only grabs up to 1024 as expected) and the remainder of the message is in the next iteration of the loop.
This makes its it impossible to process the feedback line by line.
Is there a better way to constantly read the data without it getting trunked? Is it possible to figure out the size of the response before receiving it so the buffer can be set dynamically?
TCP is a stream-based protocol. Buffer the bytes received and only extract complete messages from the stream.
For complete lines, look for newline characters in the buffer.
Example server:
import socket
class Client:
def __init__(self,socket):
self.socket = socket
self.buffer = b''
def getline(self):
# if there is no complete line in buffer,
# add to buffer until there is one.
while b'\n' not in self.buffer:
data = self.socket.recv(1024)
if not data:
# socket was closed
return ''
self.buffer += data
# break the buffer on the first newline.
# note: partition(n) return "left of n","n","right of n"
line,newline,self.buffer = self.buffer.partition(b'\n')
return line + newline
srv = socket.socket()
srv.bind(('',5000))
srv.listen(1)
conn,where = srv.accept()
client = Client(conn)
print(f'Client connected on {where}')
while True:
line = client.getline()
if not line:
break
print(line)
Example client:
s=socket()
s.connect(('127.0.0.1',5000))
s.sendall(b'line one\nline two\nline three\nincomplete')
s.close()
Output on server:
Client connected on ('127.0.0.1', 2667)
b'line one\n'
b'line two\n'
b'line three\n'

Python 3.4 - How to 'run' another script python script continuously, How to pass http get / post to socket

This question is two-fold.
1. So I need to run code for a socket server that's all defined and created in another.py, Clicking run on PyCharm works just fine, but if you exec() the file it just runs the bottom part of the code.
There are a few answers here but they are conflicting and for Python 2.
From what I can gather there are three ways:
- Execfile(), Which I think is Python 2 code.
- os.system() (But I've seen it be said that it's not correct to pass to the OS for this)
- And subprocess.Popen (unsure how to use this either)
I need this to run in the background, it is used to create threads for sockets for the recv portion of the overall program and listen on those ports so I can input commands to a router.
This is the complete code in question:
import sys
import socket
import threading
import time
QUIT = False
class ClientThread(threading.Thread): # Class that implements the client threads in this server
def __init__(self, client_sock): # Initialize the object, save the socket that this thread will use.
threading.Thread.__init__(self)
self.client = client_sock
def run(self): # Thread's main loop. Once this function returns, the thread is finished and dies.
global QUIT # Need to declare QUIT as global, since the method can change it
done = False
cmd = self.readline() # Read data from the socket and process it
while not done:
if 'quit' == cmd:
self.writeline('Ok, bye. Server shut down')
QUIT = True
done = True
elif 'bye' == cmd:
self.writeline('Ok, bye. Thread closed')
done = True
else:
self.writeline(self.name)
cmd = self.readline()
self.client.close() # Make sure socket is closed when we're done with it
return
def readline(self): # Helper function, read up to 1024 chars from the socket, and returns them as a string
result = self.client.recv(1024)
if result is not None: # All letters in lower case and without and end of line markers
result = result.strip().lower().decode('ascii')
return result
def writeline(self, text): # Helper func, writes the given string to the socket with and end of line marker at end
self.client.send(text.strip().encode("ascii") + b'\n')
class Server: # Server class. Opens up a socket and listens for incoming connections.
def __init__(self): # Every time a new connection arrives, new thread object is created and
self.sock = None # defers the processing of the connection to it
self.thread_list = []
def run(self): # Server main loop: Creates the server (incoming) socket, listens > creates thread to handle it
all_good = False
try_count = 0 # Attempt to open the socket
while not all_good:
if 3 < try_count: # Tried more than 3 times without success, maybe post is in use by another program
sys.exit(1)
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create the socket
port = 80
self.sock.bind(('127.0.0.1', port)) # Bind to the interface and port we want to listen on
self.sock.listen(5)
all_good = True
break
except socket.error:
print('Socket connection error... Waiting 10 seconds to retry.')
del self.sock
time.sleep(10)
try_count += 1
print('Server is listening for incoming connections.')
print('Try to connect through the command line with:')
print('telnet localhost 80')
print('and then type whatever you want.')
print()
print("typing 'bye' finishes the thread. but not the server",)
print("eg. you can quit telnet, run it again and get a different ",)
print("thread name")
print("typing 'quit' finishes the server")
try:
while not QUIT:
try:
self.sock.settimeout(0.500)
client = self.sock.accept()[0]
except socket.timeout:
time.sleep(1)
if QUIT:
print('Received quit command. Shutting down...')
break
continue
new_thread = ClientThread(client)
print('Incoming Connection. Started thread ',)
print(new_thread.getName())
self.thread_list.append(new_thread)
new_thread.start()
for thread in self.thread_list:
if not thread.isAlive():
self.thread_list.remove(thread)
thread.join()
except KeyboardInterrupt:
print('Ctrl+C pressed... Shutting Down')
except Exception as err:
print('Exception caught: %s\nClosing...' % err)
for thread in self.thread_list:
thread.join(1.0)
self.sock.close()
if "__main__" == __name__:
server = Server()
server.run()
print('Terminated')
Notes:
This is created in Python 3.4
I use Pycharm as my IDE.
One part of a whole.
2. So I'm creating a lightning detection system and this is how I expect it to be done:
- Listen to the port on the router forever
The above is done, but the issue with this is described in question 1.
- Pull numbers from a text file for sending text message
Completed this also.
- Send http get / post to port on the router
The issue with this is that i'm unsure how the router will act if I send this in binary form, I suspect it wont matter, the input commands for sending over GSM are specific. Some clarification may be needed at some point.
- Recieve reply from router and exception manage
- Listen for relay trip for alarm on severe or close strike warning.
- If tripped, send messages to phones in storage from text file
This would be the http get / post that's sent.
- Wait for reply from router to indicate messages have been sent, exception handle if it's not the case
- Go back to start
There are a few issues I'd like some background knowledge on that is proving hard to find via the old Google and here on the answers in stack.
How do I grab the receive data from the router from another process running in another file? I guess I can write into a text file and call that data but i'd rather not.
How to multi-process and which method to use.
How to send http get / post to socket on router, post needed occording to the router manual is as follows: e.g. "http://192.168.1.1/cgi-bin/sms_send?number=0037061212345&text=test"
Notes: Using Sockets, threading, sys and time on Python 3.4/Pycharm IDE.
Lightning detector used is LD-250 with RLO Relay attached.
RUT500 Teltonica router used.
Any direction/comments, errors spotted, anything i'm drastically missing would be greatly appreciated! Thank you very much in advance :D constructive criticism is greatly encouraged!
Okay so for the first part none of those suggested in the OP were my answer. Running the script as is from os.system(), exec() without declaring a new socket object just ran from __name__, this essentially just printed out "terminated", to get around this was simple. As everything was put into a classes already, all I had to do is create a new thread. This is how it was done:
import Socketthread2
new_thread = Socketthread2.Server() # Effectively declaring a new server class object.
new_thread.run()
This allowed the script to run from the beginning by initialising the code from the start in Socket, which is also a class of Clientthread, so that was also run too. Running this at the start of the parent program allowed this to run in the background, then continue with the new code in parent while the rest of the script was continuously active.

Resources