1

I've written a python chess game that I'd like to make playable by two people on two different computers. Without using external libraries, my first thought was to try to adapt code for a simple chat server/client to allow the game to communicate moves to both users. From my research, it seems the following code is pretty standard:

# client
from socket import *
PORT = 8000

s = socket(AF_INET, SOCK_STREAM)
s.connect(('', PORT))

while True:
    msg = raw_input('>> ')
    s.send(msg)
    reply = s.recv(1024)
    if reply:
        print '<< ' + str(reply)

s.close()

#server
from socket    import *

HOST = ''   
PORT = 8000

s = socket(AF_INET, SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(2)

conn, address = s.accept()
while True:
    print 'got a connection'
    data = conn.recv(1024)

    if data:
        print 'we got it'
        conn.sendall(data)
s.close()

My thinking is that one player (the host who invites the other player to the game), initiates the game and the server. The client then moves, sends the move to the server, and the change is reflected on the UI.

However, the infinite loops above that listen for messages/responses are giving me trouble: clearly, no other code can execute. I'd rather have the connection between client and server be in the background, or even better make use of the infinite loop already used by the UI to listen to events.

My question: is it possible to use Tkinter's mainloop to act as an infinite loop to listen for incoming data and replies? Or, would it be better to run the server and the client as subprocesses. I guess what I'd really like is to treat the listening/receiving for moves as events: when a move is made, a message is sent, and it is "handled" by the client.

Note: this is the first time I've ever used the socket library -- it would be great if any answers could respect my relative naiveté with the topic.

Edit: I think what I might need is asynchat, but I would very much appreciate understanding how this code might be adapted.

Final remarks: gordosac's answer provided some great ideas to search. I ended up finding exactly what I need here

rookie
  • 2,783
  • 5
  • 29
  • 43
  • 1
    The first thing I looked for when I saw this question was an easy way to make Tkinter listen for events on a particular file handle. Fortunately, Tcl underlying Tk does have such functionality. Unfortunately, it is exposed only as [a C function](http://www.tcl.tk/man/tcl8.6/TclLib/CrtFileHdlr.htm). You might be able to call it appropriately with [`ctypes`](https://docs.python.org/3/library/ctypes.html), but I can’t imagine you’ll get very nice code with that approach… Gosh, though, it really seems like there should be an easy way to hook into the loop from Python. – icktoofay Jul 08 '14 at 03:20
  • @icktoofay: That's such a bummer! Thank you, but I think I'm going to try using asyncore and asynchat -- this seems like it might be the way to go. – rookie Jul 08 '14 at 03:23
  • Asyncore and asynchat is certainly a good way to go—but the problem arises when you want to change something in the user interface in response to a network event. That needs some sort of synchronization between the Tk event loop and Python. I guess as a stopgap solution you could always use a timer. – icktoofay Jul 08 '14 at 03:29
  • The first thing you should do is forget the chess game and the associated complexity of GUIs and write a chat server/client which runs in a normal console. Once you understand how that works you can learn how to bring the GUI into the mix. It is extremely important to realize that the chess game can and should exist _without the GUI_. Just write the chess game itself with no GUI, write the chat server/client, and at the end bring in the GUI. Do not use asyncore. Check out David Peticolas's tutorial on Twisted. – DanielSank Jul 08 '14 at 04:41
  • 1
    Clarification: asyncore is a great way to learn how server/client code works, but it's not particularly good for real applications. – DanielSank Jul 08 '14 at 04:42
  • already written the chess game! And I think I have something working (that I understand) which makes use of @gordosac's answer below. I'll try to post something of more substance tomorrow, but if you're inclined to see the path I took, please consult this [resource](http://joyrex.spc.uchicago.edu/bookshelves/python/cookbook/pythoncook-CHP-9-SECT-7.html) – rookie Jul 08 '14 at 04:56
  • 1
    The solution used in that resource is perfectly fine. I strongly, recommend that you Google "David Peticolas Twisted" and read/do the first few chapters. It is by far the best introduction to asynchronous programming I have ever seen. If you don't like it, you can punch me in the face. If you learn nothing beyond what a reactor loop is and how to use one, it's worth your time. On the other hand, you might start using Twisted in your applications. Twisted integrates with GUI event loops easily, and makes all this stuff with passing information between threads unnecessary. – DanielSank Jul 08 '14 at 05:01

1 Answers1

1

Take a look at this question on SO:

How to use threading in Python?

You would do something like this for the client:

import threading
import os
import time

# this function meant to be started in a separate thread
get_raw_input_and_send():
    #put while loop here

if __name__ == "__main__":
    t = threading.Thread(target=get_raw_input_and_send)
    t.daemon = True
    t.start()
    while True:
        if not t.isAlive():
            os.sys.exit(-1)
        time.sleep(5)

Then on the server side do the equivalent but with the while loop from the server code

Community
  • 1
  • 1
gordosac
  • 26
  • 3
  • Thank you, but if you look at my comment below, I've tried this and it doesn't work. For example, if you put the while loop code in a separate function `receive_thread`, and start this thread, you won't execute code below the call to start this thread. – rookie Jul 08 '14 at 03:03
  • Hmm I believe what's happening is that your main thread exits (assuming there's no while loop in your main thread to keep it running). What you can do is put a while loop in your main thread so it doesn't exit. See edited answer. Another option would be to join the thread such that the main thread doesn't exit until your spawned thread does. – gordosac Jul 08 '14 at 03:33
  • I think you want to remove `t.daemon = True`. This seemed to work for me (I don't quite understand `daemon`). Try running this main method and you'll see we get the desired behavior: `if __name__ == "__main__": t = Thread(target=receive_thread) t.start() while True: print 'hello'` – rookie Jul 08 '14 at 03:44
  • before I didn't have a main method. I believe this is the problem. – rookie Jul 08 '14 at 03:46
  • Setting daemon to true indicates that you want the thread to die if the main thread dies. – gordosac Jul 08 '14 at 12:29