Home > Software engineering >  python socket & Tkinter: Starting tkinter class not working in a python socket program
python socket & Tkinter: Starting tkinter class not working in a python socket program

Time:07-08

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()
  • Related