Hello I'm trying to do an online python game with socket and tkinter. Here are my 2 files: server.py
and client.py
.
Don't worry of the lots of code, there is an explanation at the end ;)
server.py
:
import socket
import multiprocessing
from tkinter import *
import pickle
def foo(l):
return list(map(int, l))
IP = #<my_IPv4>
PORT = 1234
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((IP, PORT))
msg = "[800, 400]"
s.listen(1)
conn, addr = s.accept()
receivedPos = []
changePosCount = 0
print("Line 22")
def ReceivePosition():
while True:
receivedPos = pickle.loads(conn.recv(20))
if not receivedPos:
break
else:
changePosCount = changePosCount 1
print("Line 30")
changingPos = multiprocessing.Process(target=ReceivePosition)
changingPos.start()
print("Line 33")
while changePosCount == 0:
continue
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.title("1- Tkinter Changing Position")
self.minsize(500,400)
print("Line 43")
self.createLabel()
print("Line 45")
def createLabel(self):
self.label = Label(self, text = str(receivedPos).replace('[', '').replace(']', '') , bg='black', fg='blue', font='Calibri 40 bold')
print("Line 48")
self.label.place(x=receivedPos[0], y=receivedPos[1])
print("Line52")
root = root()
root.mainloop()
print("Line 55")
client.py
:
import socket
import pickle
def foo(l):
return list(map(int, l))
IP = #<my_IPv4>
PORT = 1234
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
while True:
enteredPos = foo(str(input("New Position (Separate x and y with a \",\", eg: 40,60): ")).split(','))
enteredPos_bytes = pickle.dumps(enteredPos)
s.sendall(enteredPos_bytes)
print(f"You placed the label at x={enteredPos[0]}, y={enteredPos[1]}")
Explanation:
At first, I have 2 empty variables: receivedPos
and changePosCount
: so the function ReceivePosition
will receive the position given by the client and assign it to receivedPos
and will then add 1 to changePosCount
. This function will to run in parallel to the rest of the program, but only after that the client enter a position.
Now that the client has entered a position that has been assigned to receivedPos
, and that changePosCount
is greater than 0, we have 2 pieces of code that are running in parallel: the ReceivePosition
function and the code that set up the tkinter GUI.
As you can see I've made so that so that every few lines of code it prints which line is executed, and I deduced that the program is returning the error when it should create the GUI (at line 36). Here's the error:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\multiprocessing\spawn.py", line 116, in spawn_main
exitcode = _main(fd, parent_sentinel)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\multiprocessing\spawn.py", line 125, in _main
prepare(preparation_data)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\multiprocessing\spawn.py", line 236, in prepare
_fixup_main_from_path(data['init_main_from_path'])
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path
main_content = runpy.run_path(main_path,
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 288, in run_path
return _run_module_code(code, init_globals, run_name,
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 97, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 87, in _run_code
exec(code, run_globals)
File "d:\Users\Jean Paul\OneDrive\Programming\JP\Socket\Exercises\1- Tkinter Changing Position\server.py", line 13, in <module>
s.bind((IP, PORT))
OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted
I googled to see what this error means and apparently it shows up when the used port isn't available, but it gives me this error for any port, available or not.
Thank you for reading my question and for trying to help me.
CodePudding user response:
You can accomplish this with threads. This is a very rough example from hacking on your code. Run the server, then the client, then type some coordinates. I dropped pickle since it isn't secure and you can just send the input text and parse it. I used socket.makefile
and its .readline()
method which will read up to a sent newline, so multiple messages can be sent if needed. TCP is a byte streaming protocol and only guarantees bytes sent are received in the same order, but you need to build a protocol on top of that to delimit messages for reliability.
client.py
import socket
HOST = 'localhost'
PORT = 1234
s = socket.socket()
s.connect((HOST, PORT))
while True:
enteredPos = input('New Position (Separate x and y with a comma, e.g.: 40,60): ')
s.sendall(enteredPos.encode() b'\n')
x,y = enteredPos.split(',')
print(f'You placed the label at {x=}, {y=}')
server.py
import socket
import threading
from tkinter import *
HOST = ''
PORT = 1234
s = socket.socket()
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
receivedPos = ''
def receive(conn,addr):
with conn, conn.makefile('r',encoding='utf8') as reader:
while True:
global receivedPos
receivedPos = reader.readline()
print(receivedPos)
if not receivedPos:
break
x,y = [int(n) for n in receivedPos.split(',')]
root.label.config(text=receivedPos)
root.label.place(x=x, y=y)
class Root(Tk):
def __init__(self):
super().__init__()
self.title("1- Tkinter Changing Position")
self.minsize(500,400)
self.label = Label(self, text = receivedPos, bg='black', fg='blue', font='Calibri 40 bold')
root = Root()
threading.Thread(target=receive, args=(conn, addr), daemon=True).start()
root.mainloop()