How connect my Kivy client to a server (TCP, Sockets) - python-3.x

So as part of my project (2D Multiplayer Card Game), I've figured out how to host and run a server script online. My plan is to have two separate kivy clients connect to the server (which will just be a script with commands).
However I'm somewhat confused about the order of operations because I think the client connection is potentially in conflict with the message loop so I'm wondering if someone could basically tell me what I should be doing:
I'm going to be using this as my serverscript:
import socket
serversocket = socket.socket()
host = 'INSERTIPHERE'
port = PORTHERE
serversocket.bind(('', port))
serversocket.listen(1)
while True:
clientsocket,addr = serversocket.accept()
print("got a connection from %s" % str(addr))
msg = 'Thank you for connecting' + "\r\n"
clientsocket.send(msg.encode('ascii'))
clientsocket.close()
This is my client connection function
def Main():
host = 'INSERTIPHERE'
port = PORTHERE
mySocket = socket.socket()
mySocket.connect((host, port))
message = input(' -> ')
while message != 'q':
mySocket.send(message.encode())
data = mySocket.recv(1024).decode()
print('Received from server: ' + data)
message = input(' -> ')
mySocket.close()
Note: I understand that the server and client aren't perfectly aligned in functions but provided I can at least a connection confirmation for now, I can work from there.
I'm basically wondering how do I put this code into a simple kivy app like this:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class BoxWidget(BoxLayout):
pass
class BoxApp(App):
def build(self):
return BoxWidget()
if __name__ == '__main__':
BoxApp().run()
My best guess is that you want to:
Establish the connection before opening the client
Passing the server connection to the primary widget (in this case the Box Widget) as you're running an instance of the client (ie BoxApp(server).run()?)
Use that connection in a message loop function of the BoxWidget
I also understand that Kivy has built in solutions with Twisted but I'm having trouble with the python 2-3 differences.
Thank you for reading.
Just to clarify: All I want to do right now is open a blank window and also have a confirmation message sent to the command line (or failing that a label in the window).

You can use threading so you don't interrupt the main thread in kivy.
I rewrote your example a bit, so what you send from the server, will be the text of a label.
server.py
import socket
serversocket = socket.socket()
host = 'localhost'
port = 54545
serversocket.bind(('', port))
serversocket.listen(1)
clientsocket,addr = serversocket.accept()
print("got a connection from %s" % str(addr))
while True:
msg = input("> ") + "\r\n"
clientsocket.send(msg.encode('ascii'))
client.py
import socket
class MySocket:
def __init__(self,host="localhost",port=54545):
self.sock = socket.socket()
self.sock.connect((host, port))
def get_data(self):
return self.sock.recv(1024)
main.py
from kivy.app import App
from kivy.uix.label import Label
from client import *
from threading import Thread
class MyLabel(Label):
def __init__(self, **kwargs):
super(MyLabel,self).__init__(**kwargs)
self.sock = MySocket()
Thread(target=self.get_data).start()
def get_data(self):
while True:
self.text = self.sock.get_data()
class BoxApp(App):
def build(self):
return MyLabel()
if __name__ == '__main__':
BoxApp().run()
Now just run server.py in one terminal, then main.py from another

I got a basic version of it working with buttons. Both on local machine and online. This Solution is likely not viable for many real time apps or even a chat server since the reply has to be initiated. However for my goal of a multiplayer card game it should more than suffice with proper conditionals.
video of test on local machine
EDIT: In the video I talk about double clicking. I have just realized this is because the first click is putting the window back in focus.
EDIT 2: Using TextInput in kv file instead of input in Python file.
server script:
import socket
def Main():
host = '127.0.0.1'
port = 7000
mySocket = socket.socket()
mySocket.bind((host,port))
mySocket.listen(1)
conn, addr = mySocket.accept()
print ("Connection from: " + str(addr))
message = 'Thank you connecting'
conn.send(message.encode())
while True:
data = conn.recv(1024).decode()
strdata = str(data)
print(strdata)
reply = 'confirmed'
conn.send(reply.encode())
mySocket.close()
if __name__ == '__main__':
Main()
This is a pretty simple server. Listen for a single client, confirm connection, open a send and receive message loop.
This is the client script which isn't hugely complicated really:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
import socket
class BoxWidget(BoxLayout):
s = socket.socket()
host = '127.0.0.1'
port = 7000
display = ObjectProperty()
def connect_to_server(self):
# called by a Button press
# Connects to the server
self.s.connect((self.host, self.port))
# Receives confirmation from Server
data = self.s.recv(1024).decode()
# Converts confirmation to string
strdata = str(data)
# Prints confirmation
print(strdata)
def send_message(self):
# Is called by the function below
# Encodes and sends the message variable
self.s.send(self.message.encode())
# Waits for a reply
self.receive_message()
def message_to_send(self):
# Defines Message to send
self.message = self.display.text
# Calls function to send the message
self.send_message()
# Note
# When I used message = input directly in send_message,
# the app would crash. So I defined message input
# in its own function which then calls the
# send function
# message_to_send is the function actually
# called by a button press which then
# starts the chain of events
# Define Message, Send Message, get Reply
def receive_message(self):
# Decodes a reply
reply = self.s.recv(1024).decode()
# Converts reply to a str
strreply = str(reply)
# prints reply
print(strreply)
class ServerApp(App):
def build(self):
box = BoxWidget()
return box
if __name__ == '__main__':
ServerApp().run()
Edit: Forgot to include the kv file
<BoxWidget>:
display: display
Button:
text: 'Hello'
on_press: root.message_to_send()
Button:
text: 'Connect'
on_press: root.connect_to_server()
TextInput:
id: display
In future iterations, I'll be replacing print statements with conditionals (ie did client one draw a card? if so client 2's opponent draws a face-down card etc).
Relatively rudimentary as it is now but there is a lot you could do from here.

Related

Sending Mouse and keyboard inputs from server to client over socket in Python?

I have my desktop stream working the server can see the client desktop, what I basically do is grab screenshots from client and send it to server continuously and displaying it using cv2.imshow().
Now I want a way to send mouse and keyboards inputs from server to the client so that it can control the client remotely. I know pyautogui module could be used to simulate moving, click and dragging mouse as well as pressing keys.
One way I thought is to send the coordinates in fractions of the resolution.
Example:
If your picturebox is 800x600 and you click at location (200, 300), send (0.25, 0.5).
On the other machine, you receive (0.25, 0.5) and you multiply them by the resolution. On a 1024x768, you move the mouse pointer to location (256, 384).
But I need a method to get the mouse and keyboard input from the opencv window and send it to the client over the socket and then simulate it on client side.
Any other method instead of using opencv to display the frames on server would be accepted as well, as long as I am able to work with keyboard and mouse inputs and improves the performance.
Note: I am also not able to get the screenshot of the UAC prompt using Imagegrab.grab() even if I run the script as an administrator I don't know why
this is happening.
Below is my code for client and server exceptions which are not being handled as I am just trying out that the things work.
Any suggestions to make my stream faster are welcomed as well :)
server.py:
import socket
import struct
import cv2
import numpy
host = "192.168.0.5"
port = 4444
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #create socket
s.bind((host,port))
s.listen(5)
c , addr = s.accept()
print("Connection recieved from {}".format(addr))
cv2.namedWindow("Remote Desktop",cv2.WINDOW_NORMAL)
while True:
bs = c.recv(8)
(length,) = struct.unpack(">Q",bs) # get length of image
data = b''
while(len(data)<length): # get image data
recv_data = length - len(data)
if(recv_data>4096):
data += c.recv(4096)
else:
data += c.recv(recv_data)
with open("demo.jpg","wb") as f:
d = f.write(data)
#print(d)
img = cv2.imread("demo.jpg")
cv2.imshow("Remote Desktop",img)
key = cv2.waitKey(1)
if key == 27:
break
cv2.destroyAllWindows()
s.close()
Here is my client.py:
import socket
import struct
from PIL import ImageGrab
import time
host = "192.168.0.5"
port = 4444
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #create socket
s.connect((host,port)) #connect to server
print("Connection Established")
time.sleep(5)
while True:
img = ImageGrab.grab()
img.save("demo.jpg","JPEG")
with open("demo.jpg","rb") as f:
data = f.read() # read the image
s.sendall(struct.pack(">Q",len(data))) #send the length of image in bytes
s.sendall(data) # send the data
s.close()
So how can I send mouse and keyboard inputs effectively over socket in python? Thanks for your help.
This can help you out....Make it according to you need.
import threading
import mouse
import keyboard
mouse_events = []
mouse.hook(mouse_events.append)
keyboard.start_recording()
keyboard.wait("*")
mouse.unhook(mouse_events.append)
keyboard_events = keyboard.stop_recording()
#Keyboard threadings:
k_thread = threading.Thread(target = lambda :keyboard.play(keyboard_events))
k_thread.start()
#Mouse threadings:
m_thread = threading.Thread(target = lambda :mouse.play(mouse_events))
m_thread.start()
#waiting for both threadings to be completed
k_thread.join()
m_thread.join()

My server coded in pyhon 3 does not send messages to all users

I started learning socket programming in Python 3 and I am trying to create a TCP chat server which can support multiple users but it is not working because the server does not send the message to all users, it sends it back the message only to the person who sent the message.
This is my server code (sorry for being a noob):
import socket
from _thread import *
sk = socket.socket()
sk.bind(('', 3000))
sk.listen(5)
def client(conn):
while True:
data = conn.recv(1024)
if not data:
break
mess = data.decode('utf-8')
print(mess)
conn.sendall(data)
if __name__ == '__main__':
print("server started")
while True:
conn, id = sk.accept()
print("{} conected".format(id))
start_new_thread(client, (conn,))
and this is my client :
import socket
import threading
sk = socket.socket()
target_ip = input("ip : ")
port = int(input("port: "))
sk.connect((target_ip, port))
def send():
mess = input()
while mess != 'EXIT':
mess = str.encode(mess)
sk.send(mess)
mess = input()
def listen():
while True:
data = sk.recv(1024)
mess = data.decode('utf-8')
if mess == 'zalinga':
quit()
else:
print(mess)
if __name__ == '__main__':
t1 = threading.Thread(target = send)
t2 = threading.Thread(target = listen)
t1.start()
t2.start()
I don't know where is the problem but I don't get any errors.
Before you start your new thread to listen to the connection, you need to add that connection to some collection of connections.
When you receive data, instead of sending it to the socket that sent you the data, iterate over the collection and send the data to each socket in the list. Consider also sending at the same time the id of the socket, so you know who sent you the info.

read MFRD522 data using raspberry pi and send this data by http(python3)

I'm working on a project and I want to use RFID as a position reference(when raspberry pi and MFRC522 read the data, send it to server and make sure where is the position or adjust position while RFID reader is fixed)
My code is below:
import RPi.GPIO as GPIO
import MFRC522
import signal
import socket
HOST = '192.168.11.48'
PORT = 9009
continue_reading = True
# Capture SIGINT for cleanup when the script is aborted
def end_read(signal,frame):
global continue_reading
print("Ctrl+C captured, ending read.")
continue_reading = False
GPIO.cleanup()
# Hook the SIGINT
signal.signal(signal.SIGINT, end_read)
# Create an object of the class MFRC522
MIFAREReader = MFRC522.MFRC522()
# Welcome message
print("Welcome to the MFRC522 data read example")
# This loop keeps checking for chips. If one is near it will get the UID and authenticate
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as sock:
sock.connect((HOST,PORT))
#print("closing client")
while True:
while continue_reading:
# Scan for cards
(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
# If a card is found
if status == MIFAREReader.MI_OK:
print("Card detected")
# Get the UID of the card
(status,uid) = MIFAREReader.MFRC522_Anticoll()
# If we have the UID, continue
if status == MIFAREReader.MI_OK:
# Print UID
print("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]))
# This is the default key for authentication
key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
# Select the scanned tag
MIFAREReader.MFRC522_SelectTag(uid)
# Authenticate
status = MIFAREReader.MFRC522_Auth(MIFAREReader.PICC_AUTHENT1A, 8, key, uid)
data = "str(uid[0])"
msg = data
sock.sendall(msg.encode())
data = sock.recv(128)
print('date from Echo server is [%s]'%data.decode())
# Check if authenticated
if status == MIFAREReader.MI_OK:
MIFAREReader.MFRC522_Read(8)
MIFAREReader.MFRC522_StopCrypto1()
else:
print("Authentication error")
I am using echo server to make sure that it's working
code for echo server is:
import socketserver
HOST = ''
PORT = 9009
class MyTcpHandler(socketserver.BaseRequestHandler):
def handle(self):
print("[%s]conneted"%self.client_address[0])
try:
while True:
self.data = self.request.recv(1024)
if self.data.decode() == "/quit":
print('ended by user [%s]'%self.client_address[0])
return
print('[%s]'%self.data.decode())
self.request.sendall(self.data)
except Exceptions as e:
print(e)
def runServer():
print("starting echo server")
print("if you want to close echo server, click Ctrl+C")
try:
server = socketserver.TCPServer((HOST,PORT), MyTcpHandler)
server.serve_forever()
except KeyboardInterrupt:
print("--- closing server")
runServer()
I ran fist piece of code while the second one was running, but only result I got was -
Welcome to the MFRC522 data read example
Card detected
Card read UID:178,29,209,48
size:8.
Anyone had idea what I should change to make it work?
thanks

How to constantly receive requests from socket python

I'm learning about sockets in python and as practice I made a python gui app with a text box, two entry boxes and a button. It works like a chat app, I can run many modules at once, and in each, when a user enters text and click send, it shows the message entered, on the text box of the user's module and all other modules open. I've gotten most of it working but the issue is that it only updates the text box when the send button is pressed but I want to update constantly so as soon as a new message is sent, it shows it on the screen.
Here's my code so far:
#!/usr/bin/python
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.root = tk.Tk()
self.create_widgets()
def create_widgets(self):
... #code that creates widgets shortended for readability
#text box widget named self.txt
self.send_btn["command"] = self.send_msg //this handles sending messages to the server when the button is pressed
... #code that creates widgets shortended for readability
def send_msg(self):
s = socket.socket()
host = socket.gethostname()
port = 9999
address = (host, port)
msg=self.name_bar.get() + ": " +self.input_bar.get()
#name bar is another widget so I can enter an identity for each module
#input bar is the entry box where text is entered in
self.input_bar.delete(0, len(msg))
s.connect(address)
s.send(msg.encode())
#Wrote this code that updates textbox when button pushed
#self.txt.insert(tk.END, s.recv(1024))
#self.txt.insert(tk.END, "\n")
#Method I created to call constantly to update textbox
def rcv_msg(self):
s = socket.socket()
host = socket.gethostname()
port = 9999
address = (host, port)
s.connect(address)
if s.recv(1024).decode() != "":
#self.txt.insert(tk.END, s.recv(1024))
#self.txt.insert(tk.END, "\n")
Also I been doing Java lately so I apologise if my terminology is mixed up.
I've already made the method to update the text box, I just don't know how to call it, I tried a while loop but it just stops the app from running. Also, the server is really simple and just sends the message from the client above to all the all modules of the client open. I din't know if the code was necessary, I was told previously to try to keep questions short but if it's needed please tell me. Thank you!
You could have something listening for new information. A separate thread that doesn't interfere with the GUI. (Pseudo code ahead!)
import socket
from threading import Thread, Lock
import time
class ListenThread(Thread):
def __init__(self, app, lock):
Thread.__init__(self)
self._app = app
self._lock = lock
self._terminating = False
def destroy(self):
self._terminating = True
def run(self):
s = socket.socket()
host = socket.gethostname()
port = 9999
address = (host, port)
while True:
if self._terminating:
break;
if s.recv(1024).decode() != "":
self._lock.acquire()
self._app.post_new_data(s.recv(1024))
self._lock.release()
time.sleep(0.1)
class App(object):
# ...
def __init__(self):
self._lock = Lock()
self._listener = ListenThread(self, self._lock)
self._listener.start()
def post_new_data(self, data):
# Post information to the GUI
def all_data(self):
self._lock.acquire()
# ...
self._lock.release()
def destroy(self):
self._listener.destroy()
# ... Tk mainloop ...
Breakdown
class ListenThread(Thread):
This is the item that will listen for new data coming from other connections and post them to the GUI via self._app.post_new_data(...) call in the run(self) operation.
def run(self):
s = socket.socket()
When we first start the execution on a thread (via start()) we create our socket and get a connection going. This way, all incoming transmissions will route through this in order to keep our GUI free for things it likes to do (paint the interface, take on user input, etc.)
The while loop on the ListenThread.run will keep looking for new data until we've killed the thread. As soon as it receives data, it will push that information to our App. In the post_new_data function, you can add our new data onto the GUI, store it, whatever you would like.
def post_new_data(self, data):
self.txt.insert(tk.END, data)
self.txt.insert(tk.END, "\n")

force socket to timeout in python

I'm using a socket to listen on a port in a while loop, with a 5 second timeout set by socket.settimeout(). But I have another method, which set's the listening port, and when called with a new port, i wanna force the socket to timeout so that I can reinitialise the socket and set the appropriate port inside the while loop. Is there a way to do that?
The socket is inside a subclass of threading.Thread
PS. Since this is my second day with Python, any other suggestions regarding any part would be most welcome. Thank you
EDIT:
I almost forgot. I want to reinitialise the socket when the setoutboundport method is called.
EDIT2
Man the whole code is messed up. I reexamined everything and it's really wrong for what I wanna achieve. Just focus on the main question. Timing out the socket.
import threading
import socket
import ResponseSender
import sys
import traceback
def __init__(self, inboundport, outboundip, outboundport, ttl=60):
super(Relay, self).__init__()
self.inboundport = inboundport
self.outboundip = outboundip
self.outboundport = outboundport
self.ttl = ttl
self.serverOn = True
self.firstpacket = True
self.sibling = None
self.newoutboundport = 0
self.listener = None
# ENDOF: __init__
def stop(self):
self.serverOn = False
# ENDOF: stop
def setsiblingrelay(self, relay):
self.sibling = relay
# ENDOF: setsiblingrelay
def setoutboundport(self, port):
self.newoutboundport = port
# ENDOF: setoutboundport
def run(self):
s = None
try:
while self.serverOn:
if not s:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if self.outboundport != self.newoutboundport:
try:
s.close()
except:
pass
s.settimeout(4)
s.bind(('', self.inboundport))
print("Relay started :{0} => {1}:{2}".format(self.inboundport, self.outboundip, self.outboundport))
print("---------------------------------------- LISTENING FOR INCOMING PACKETS")
data, address = s.recvfrom(32768)
print("Received {0} from {1}:{2} => sending to {3}:{4}"
.format(data, address[0], address[1], self.outboundip, self.outboundport))
ResponseSender.sendresponse(address[0], address[1], data)
except TimeoutError:
pass
except:
print("Error: {0}".format(traceback.format_exception(*sys.exc_info())))
# ENDOF: run

Resources