recently I've been experimenting with Twisted (python library) in an attempt to make a TCP chat server/client. I had the server running nicely but when I tried to add a Tkinter-based GUI to the server, things got weird. As soon as a user connects to the server, a message is sent to the GUI. However, somewhere along the way something goes wrong and a long-winded error comes up, the gist of which is that Tkinter ran out of stack space because of an infinite loop. I've put my code below. The function that I am having trouble with is App.write(text) and User.connectionMade(*args) as well as any other function in the User class that attempts to print text to the GUI.
from twisted.internet.protocol import ServerFactory, Protocol
from twisted.internet import reactor
from os import path
import yaml
import threading
from Tkinter import *
__version__ = ''
__author__ = ''
class User(Protocol):
def connectionMade(self,*args):
self.gui.write('New connection from %s' % (self.addr.host))
self.transport.write('Username: ')
def connectionLost(self,reason):
self.gui.write('Connection lost with %s' % (self.addr.host))
if not self.name == None:
msg = '%s has disconnected\r\n' % (self.name)
self.gui.write(msg.rstrip())
self.toAll(msg)
del self.users[self.name]
def dataReceived(self,data):
if data == '\x08':
if len(self.text) > 0:
self.text = self.text[:-1]
return
elif not data.endswith('\r\n'):
self.text += data
return
if self.name == None:
self.setName(self.text)
else:
self.handle(self.text)
self.text = ''
def handle(self,data):
if not data.startswith('/'):
self.chat(data)
else:
self.gui.write('%s executed command %s' % (self.name, data))
if data in ['/help','/h']: self.cmdHelp()
elif data in ['/list','/l']: self.userList()
elif data in ['/motd','/m']: self.sendMotd()
elif data in ['/ping','/p']: self.transport.write('Pong!\r\n')
else: self.transport.write('Unrecognized command %s\r\n' % (data))
def cmdHelp(self):
x = ['\r\nCOMMANDS:',\
'/motd,/m - Display the MOTD',\
'/list,/l - Display a list of online users',\
'/help,/h - Display this list\r\n']
for item in x:
self.transport.write(item+'\r\n')
def sendMotd(self):
self.transport.write('\r\nMOTD: %s\r\n\r\n' % (self.motd))
def userList(self):
self.transport.write('\r\nCURRENTLY ONLINE: server,%s\r\n\r\n' % (','.join(item for item in self.users)))
def setName(self,name):
if self.users.has_key(name) or name.lower() == 'server':
self.transport.write('That username is in use!\r\nUsername: ')
elif ' ' in name:
self.transport.write('No spaces are allowed in usernames!\r\nUsername: ')
elif name == '':
self.transport.write('You must enter a username!\r\nUsername: ')
else:
self.users[name] = self
self.name = name
self.gui.write('New user registered as %s' % (name))
self.toAll('%s has connected' % (self.name))
self.transport.write('\nSuccessfully logged in as %s\r\n\r\n' % (name))
self.sendMotd()
def toAll(self,msg):
for name,protocol in self.users.iteritems():
if not protocol == self:
protocol.transport.write(msg)
def chat(self,data):
to_self = '<%s (you)> %s\r\n' % (self.name, data)
to_else = '<%s> %s\r\n' % (self.name, data)
self.gui.write('[CHAT] - %s' % (to_else.rstrip()))
self.transport.write(to_self)
self.toAll(to_else)
def __init__(self,addr=None,users=None,motd=None,master=None):
self.name = None
self.addr = addr
self.users = users
self.motd = motd
self.text = ''
self.factory = master
self.gui = self.factory.app
self.kicked = False
class App(Frame):
def write(self,text):
self.display.insert(END,text+'\n')
def clear(self,event=None):
self.display.delete(1.0,END)
def userList(self):
self.write('Currently online: server,%s' % (','.join(item for item in self.factory.users)))
def handle(self,event=None):
msg = self.entry.get()
self.entry.delete(0,END)
if not msg.startswith('/'): self.send(msg)
elif msg in ['/cls','/clear','/clr','/c']: self.clear()
elif msg in ['/list','/l']: self.userList()
elif msg in ['/exit']: self.kill()
else: self.write('Unrecognized command \'%s\'' % (msg))
def send(self,msg,event=None):
for item in self.factory.users: self.factory.users[item].transport.write('<server> %s\r\n' % (msg))
self.write('[CHAT] - <server> %s' % (msg))
def kill(self):
self.write('Stopping server...')
reactor.stop()
self.write('GUI says guidbye! :(')
self.quit()
def __init__(self,master,factory):
Frame.__init__(self,master)
self.grid(row=0,sticky=N+E+S+W)
self.columnconfigure(0,weight=1)
self.rowconfigure(0,weight=1)
self.display = Text(self)
self.display.grid(row=0,sticky=N+E+S+W)
self.yscroll = Scrollbar(self,command=self.display.yview)
self.yscroll.grid(row=0,column=1,sticky=N+S)
self.display.config(yscrollcommand=self.yscroll.set)
self.entry = Entry(self)
self.entry.grid(row=1,sticky=E+W)
self.master = master
self.master.wm_title('TCP Chat Server v%s' % (__version__))
self.factory = factory
self.motd = ''
self.port = 0
self.entry.bind('<Return>',self.handle)
self.master.protocol('WM_DELETE_WINDOW',self.kill)
self.write('TCP Chat Server v%s' % (__version__))
self.write('by %s\n' % (__author__))
self.write('Server currently running on port %s' % (self.factory.port))
class Main(ServerFactory):
def buildProtocol(self,addr):
return User(addr=addr,users=self.users,motd=self.motd,master=self)
def start(self):
self.root = Tk()
self.root.columnconfigure(0,weight=1)
self.root.rowconfigure(0,weight=1)
self.app = App(self.root,self)
self.app.mainloop()
def __init__(self,motd,port):
self.users = {}
self.motd = motd
self.port = port
self.tk_thread = threading.Thread(target=self.start)
self.tk_thread.start()
if not path.isfile('config.yml'):
open('config.yml','w').write('port: 4444\nmotd: No motd set!')
with open('config.yml','r') as f:
dump = yaml.load(f.read())
motd = dump['motd']
port = dump['port']
reactor.listenTCP(port,Main(motd,port))
reactor.run()
Everything else is running as expected, and when I comment out the App.write('') statements, the program runs as expected (sans GUI and server-side messages). I've been using Windows to test the program so I use
telnet localhost 4444
to run the client.
Twisted has some specialised support for Tkinter.
from Tkinter import *
from twisted.internet import tksupport, reactor
root = Tk()
# Install the Reactor support
tksupport.install(root)
# at this point build Tk app as usual using the root object,
# and start the program with "reactor.run()", and stop it
# with "reactor.stop()".
Related
No matter what port I set Python says its being used I don't understand why, I'm using twisted
Sat Aug 26 12:49:31 2017 - (/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py:980) Couldn't listen on any:4444: [Errno 98] Address already in use.
I'm not sure what code portions you need or information so if you need anything let me know.
server.py
import glob
import uuid
from modules import util
from modules import db as _db
from modules import LOG
from objects.user import User
from objects.room import Room
from objects.message import Message
from objects.Table import Table
from autobahn.twisted.websocket import WebSocketServerProtocol, \
WebSocketServerFactory, \
listenWS
def hexc(e):
et, ev, tb = sys.exc_info()
if not tb:
return str(e)
while tb:
lineno = tb.tb_lineno
fn = tb.tb_frame.f_code.co_filename
tb = tb.tb_next
return "(%s:%i) %s" % (fn, lineno, str(e))
class oChat(WebSocketServerProtocol):
_rooms = []
_userlist = Table()
_commands = Table()
_commands.user = Table()
db = _db.db('/home/chat/database.db')
def onOpen(self):
self.loadUserCommands()
self.loadSysCommands()
def getLevel(self, user):
if user.mod:
return 1
elif user.owner:
return 2
else:
return 0
def add(self, object):
if object not in self._rooms:
self._rooms.append(object)
def get(self, name):
for room in self._rooms:
if room.name == name:
return room
def execFile(self, f, dict):
with open(f, "r") as file:
try:
exec (compile(file.read(), f, 'exec'), dict)
except:
execfile(f, dict)
file.close()
return dict
def config(self, value):
config = {}
self.execFile("configuration/config.conf", config)
return config[value]
def getCommand(self, name):
name = name.lower()
if self._commands.has_key(name):
if not self._commands[name].disabled:
return self._commands[name]
def getUserCommand(self, name):
name = name.lower()
if self._commands.user.has_key(name):
if not self._commands.user[name].disabled:
return self._commands.user[name]
def setCommand(self, name, desc, func, disabled=False):
name = name.lower()
self._commands[name] = Table()
self._commands[name].desc = desc
self._commands[name].func = func
self._commands[name].disabled = disabled
def setUserCommand(self, name, desc, func, disabled=False, level=0):
name = name.lower()
self._commands.user[name] = Table()
self._commands.user[name].desc = desc
self._commands.user[name].func = func
self._commands.user[name].level = level
self._commands.user[name].disabled = disabled
def reload(self):
try:
self.loadSysCommands()
self.loadUserCommands()
except Exception as e:
print hexc(e)
def make_user(self, *args):
return User(*args)
def make_room(self, *args):
return Room(*args)
def make_message(self, *args):
return Message(*args)
def loadUserCommands(self):
files = glob.glob("protocol/user/*.py")
for file in files:
b = self.execFile(file, {})
b['init'](self)
def loadSysCommands(self):
files = glob.glob("protocol/*.py")
for file in files:
b = self.execFile(file, {})
b['init'](self)
def joinRoom(self, room, user, args):
has_sym = util.has_symbles(args, False)
room.removeUser(user, self)
room._sendCommand("uc", str(room.getCount(self)))
if args in self.db.getRooms():
room.addUser(user, self)
user.setNewRoom(room.name)
self.add(room)
room._sendCommand("uc", str(room.getCount(self)))
return True
else:
args = args.replace(" ", "-")
if not has_sym and user.status == "logged_in":
self.db.addRoom(args, user.name)
room = Room(args, self)
self.add(room)
user.setNewRoom(args)
room.addUser(user, self)
self.db.setTitle(room.name, user.name, room.name)
room._sendCommand('title', room.name)
room._sendCommand("uc", str(room.getCount(self)))
return True
else:
return False
def onConnect(self, req):
self.id = uuid.uuid4().hex
User(self.id).setIdent(db._ident(str(self.peer.split(":", 2)[1])))
User(self.id).setConnection(self.id, self)
msg = "[CONNECT] IP(%s) IDENTITY(%s)" % (str(self.peer.split(":", 2)[1]), User(self.id).ident)
print(LOG.Log(msg))
def onMessage(self, payload, isBinary):
data = payload.decode('utf8').split("::", 1)
user = User(self.id).get()
room = self.get(user.roomname)
if not room: room = Room(user.roomname.lower(), self)
try: room.check(user, self.db)
except: pass
print LOG.Log(payload.decode("utf8"))
if len(data) > 1:
cmd, args = data[0], data[1]
else:
cmd, args = data[0], ""
if cmd == "bmsg":
if args.startswith(self.config("USER_PROTOCOL_SEP")):
data = args.split(self.config("USER_PROTOCOL_SEP"), 1)
data = data[1].split(" ", 1)
if len(data) > 1:
cmd, args = data[0], data[1]
else:
cmd, args = data[0], ""
key = cmd
cmd = self.getUserCommand(key)
msg = Message(room.name, user.name, args, ident=user.ident)
if cmd and self.getLevel(user) >= cmd.level: # user protocol
try: cmd.func(self, room, user, msg, args)
except Exception as e: user._sendCommand('sysmsg', 'er', hexc(e))
else:
if not user.banned:
key = cmd
msg = Message(room.name, user.name, args, ident=user.ident) # just storing the message the bmsg.py handles sending
msg.createMessage(self.db, True)
cmd = self.getCommand(key)
if cmd: # main protocol bmsg
if user.status == 'logged_in': cmd.func(self, room, user, msg, args)
else: user._sendCommand('sysmsg', 'er', 'login or register')
else:
user._sendCommand('sysmsg', 'banned', 'you are banned') # set on sending live msg only
else:
key = cmd
cmd = self.getCommand(key)
if cmd: # main protocol other
msg = Message(room.name, user.name, args, ident=user.ident, storeMsg=False)
try: cmd.func(self, room, user, msg, args)
except Exception as e: user._sendCommand("sysmsg", "er", hexc(e))
if __name__ == '__main__':
try:
import sys
from twisted.internet import reactor,ssl
contextFactory = ssl.DefaultOpenSSLContextFactory('/etc/letsencrypt/live/otku.ga/privkey.pem',
'/etc/letsencrypt/live/otku.ga/fullchain.pem')
factory = WebSocketServerFactory(u"wss://otku.ga:4444")
factory.protocol = oChat
listenWS(factory, contextFactory)
#log.startLogging(sys.stdout)
#factory.setProtocolOptions(maxConnections=2)
reactor.listenTCP(4444, factory)
reactor.run()
except KeyboardInterrupt:
print("[ERR] KBI")
except Exception as e:
LOG.Log(hexc(e), 'er')
I don't have any errors other then the port being in use when its not.
If you need the whole server ill provide a zip with requirements.txt
You’re setting the server up to listen twice – once with listenWS and once with reactor.listenTCP. Remove reactor.listenTCP, as you want listenWS to call reactor.listenSSL for you.
There are various reasons this might happen [General Solution],
Reason 1: You may tried running your application is one of the reserved ports [0-1024] so some applications might be using the port actually.
Reason 2: You may terminated the application, so the instruction for closing the socket (e.g socket.close()) has never gets called. so the socket is open somewhere
Reason 3: Is that your only error message? Does it says anything about admin permission? Have you tried running in admin permission?
I'm looking to create a "self contained threaded class" using Python 3.
At a high level, want I would liked to do is to spawn up 50 asynchronous device objects from my "main" class and then just use their methods as needed. This is not difficult when just dealing with objects in synchronous situation but gets cloudy quite quickly as we move to asynchronous processing. The primary idea to keep the threading self contained in the device class so my base (main.py) code stays streamlined/clean and without any of the thread management.
I don't plan on any resource sharing in this case so I think I'm clear of any thread lock issues.
Here is some sample code that I hope someone can provide some hints or samples into making it a self threaded class (meaning I don't want to manage threads at the main.py level):
Sample main.py
from deviceworker import Device
availableworkers = {'USA':'services.groupkt.com', 'IND':'services.groupkt.com'}
Activeworkers = []
for name, ip in availableworkers.items():
Activeworkers.append(Device(name, ip))
for worker in Activeworkers:
worker.checkcountry() # asynchronous call - (we don't want to wait for a response)
# The idea is to keep this code as clean as possible.
Sample Object: deviceworker.py
import urllib.request
import urllib.parse
import json
class Device:
def __init__(self, name, endpoint, preamble = 'state', port = 80 ):
self.name = name
self.connected =False
self.connection = HTTPConnection(endpoint, preamble, port)
self.getStatus()
def getStatus(self, check_for = None):
self.urlresponse = json.loads(self.connection.GET('get/USA/all')) #Use USA just to verify connection
if check_for:
pass
self.connected = True
def checkcountry(self):
print(self.connection.GET('get/%s/all' % self.name))
class HTTPConnection:
def __init__(self, endpoint, preamble = None, port = 80):
if preamble: # specificing a version after the port and before method
self.url = 'http://%s:%s/%s/' % (endpoint, port, preamble)
else:
self.url = 'http://%s:%s/' % (endpoint, port)
print('_init_ url=%s' % self.url)
def GET(self, operation):
#try:
#print('%s%s' % (self.url, operation))
with urllib.request.urlopen('%s%s' % (self.url, operation)) as f:
return f.read().decode('utf-8')
#except Exception as e:
#raise Exception("GET Request Failed")
I've stripped most of the exception handling for simplicity. The sample above should work.
--- UPDATE ---
So I think I've sort of figured it out. Still not getting the parrellism I would expect from the documentation.
import threading
import urllib.request
import urllib.parse
import json
import time
class Device(threading.Thread):
def __init__(self, name, endpoint, preamble = 'state', port = 80 ):
threading.Thread.__init__(self)
self.name = name
self.connected = False
self.connection = HTTPConnection(endpoint, preamble, port)
print('%s: __init__' % self.name)
def run(self):
self.getStatus()
print('%s: hit run()' % self.name)
def getStatus(self):
self.urlresponse = json.loads(self.connection.GET('get/USA/all')) #Use USA just to verify connection
self.connected = True
def checkcountry(self):
if (self.name == 'USA'): self.waittime = 10
else: self.waittime = 0
print('%s: Getting Codes - wait time: %s' % (self.name, self.waittime))
start_time=time.time()
time.sleep(self.waittime)
result =self.connection.GET('get/%s/all' % self.name)
elapsed_time=time.time() - start_time
print('%s: Got Codes - second: %s' % (self.name, elapsed_time))
class HTTPConnection:
def __init__(self, endpoint, preamble = None, port = 80):
if preamble: # specificing a version after the port and before method
self.url = 'http://%s:%s/%s/' % (endpoint, port, preamble)
else:
self.url = 'http://%s:%s/' % (endpoint, port)
def GET(self, operation):
with urllib.request.urlopen('%s%s' % (self.url, operation)) as f:
return f.read().decode('utf-8')
DeviceList = {'USA':'services.groupkt.com', 'IND':'services.groupkt.com'}
ActiveDevices = []
DeviceList = {'USA':'services.groupkt.com', 'IND':'services.groupkt.com'}
ActiveDevices = []
for name, ip in DeviceList.items():
print('main: creating object for: %s' % name)
newDevice = Device(name, ip)
ActiveDevices.append(newDevice)
newDevice.start()
for device in ActiveDevices:
print('main: calling checkcountry() for: %s' % device.name)
device.checkcountry()
Here are the results:
main: creating object for: USA
USA: __init__
main: creating object for: IND
IND: __init__
main: calling checkcountry() for: USA
USA: Getting Codes - wait time: 10
USA: Got Codes - second: 10.167016744613647
main: calling checkcountry() for: IND
IND: Getting Codes - wait time: 0
IND: Got Codes - second: 0.11001110076904297
I by adding in the delay to the USA search I would have expected the IND to finish first but it appears that it serialized.
I'm running this on:
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
Here is a custom thread sample with locking worked great for me, better than using the event.
Try it in Colab.
import threading,time
i=0
luk=threading.Lock()
global i
global y
global t_num
class special_thread(threading.Thread):
"""This function starts a Thread class"""
def __init__(self, execute,threadID , name, daemon,args=(), repetitive=False,kwargs=None, interval_sec=60 ):
threading.Thread.__init__(self)
self.daemon = daemon
self.stopped = threading.Event()
self.interval_sec = interval_sec
self.execute = execute
self.name = name
if kwargs is None:
kwargs = {}
self.args = args
self.kwargs=kwargs
self.repetitive=repetitive
self.threadID = threadID
print(args)
def stop(self):
self.stopped.set()
self.join()
def run(self):
if self.repetitive:
while not self.stopped.wait(self.interval_sec):
self.execute(*self.args,**self.kwargs)
else:
self.execute(*self.args,**self.kwargs)
def center(t_num):
y=0
luk.acquire()
caller = inspect.getouterframes(inspect.currentframe())[1][3]
print(' {} is aquiring by {} '.format( caller, str(time.ctime())))
y+=t_num
print( "Inside %s()" % caller)
print('thread number is ',t_num,y)
time.sleep(2*t_num)
luk.release()
print(' {} is releasing by {} '.format( caller, str(time.ctime())))
def target_uno():
t_num=1
center(t_num)
def target_dos():
t_num=2
center(t_num)
target_uno=special_thread(execute=target_uno, args=(),repetitive=True, interval_sec=1,threadID=10004,
name='target_uno',
daemon=False )
target_dos=special_thread(execute=target_dos, args=(),repetitive=True, interval_sec=1,threadID=10004,
name='target_dos',
daemon=False )
if __name__ == "__main__":
target_uno.start()
target_dos.start()
time.sleep(20)
target_uno.stop()
target_dos.stop()
i am trying to receive about 1000 connections to my server but it cannot receive more than 512. What can i do to increase the amount of open connections? I am running windows 8.1
Not: I am very new to this stuff so, thanks for help
Here is my code;
import asyncore
import socket
import uuid
import time
import threading
class statistics(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
entry = raw_input("")
zaman = int(time.time())
cmd = receivedCmd
print "calculating.."
time.sleep(1)
if entry == 'istatistik':
print str(receivedCmd-cmd) + " command/second"
print "total received commands: " + str(receivedCmd)
entry = ""
class tcpClient:
def __init__(self):
self.clientid = uuid.uuid1(int(time.time()))
self.buffer = ""
self.buffer_size = 0
self.conn_time = time.time()
self.overflow = 0
#print str(self.clientid) + " assingned"
def recv_msg(self, msg):
global receivedCmd
self.buffer = msg
self.buffer_size = len(self.buffer)
receivedCmd = receivedCmd + 1
if self.buffer_size >= 1024:
self.overflow = 1
def __del__(self):
print str(self.clientid) + " has left."
class TCPHandler(asyncore.dispatcher_with_send):
global clist
def handle_read(self):
data = self.recv(1024)
if data:
if clist[self].overflow:
self.send("overflow")
self.handle_close()
else:
self.send(data)
clist[self].recv_msg(data)
def handle_close(self):
del clist[self]
self.close()
def handle_error(self):
del clist[self]
self.close()
class TCPServer(asyncore.dispatcher):
global clist
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accept(self):
self.clist = clist
pair = self.accept()
if pair is None:
pass
else:
sock, addr = pair
#print 'Connection : %s' % repr(addr)
clist[TCPHandler(sock)] = tcpClient()
if __name__ == '__main__':
clist = {}
receivedCmd = 0
server = TCPServer('', 5000)
server2 = TCPServer('',5001)
StaticsThread = statistics()
StaticsThread.start()
asyncore.loop()
Note: I still cannot receive more than 512 connections with the Twisted Framework, i don't know what to do. There have to be thousands of connected clients. Please help.
The asyncore module relies in the select OS function, which only supports a limited number of file descriptors.
As an alternative use a multi-threading server (I won't recommend this) or, better, the Twisted framework which is event-driven (highly recommended!).
Hope this helps!
Since Twisted's default reactor under Windows is also select-based then you should consider using the IOCP reactor instead.
from twisted.internet import iocpreactor
iocpreactor.install()
from twisted.internet import reactor
But also take into account that Twisted prefers Linux systems (where the default reactor is epoll-based) rather than Windows. Maybe switching to Linux is a better choice.
I've been following the tutorials and now have a twisted reactor running. I've used telnet to test that it does stuff but I've not managed to find anything in the twisted tutorials on how to connect to the reactor.
My assumption was there would be something within twisted to do this, should I instead use the built in socket?
Edit:
This is the Server script:
import time
import multiprocessing
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class TTT(LineReceiver):
def __init__(self, users):
self.users = users
self.name = None
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("You are connected")
def connectionLost(self, reason):
if self.users.has_key(self.name):
del self.users[self.name]
def lineReceived(self, line):
if line == "quit":
reactor.stop()
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
self.handle_CHAT(line)
def handle_GETNAME(self, name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another.")
return
self.sendLine("Welcome, %s!" % (name,))
self.name = name
self.users[name] = self
self.state = "CHAT"
def handle_CHAT(self, message):
message = "<%s> %s" % (self.name, message)
for name, protocol in self.users.iteritems():
if protocol != self:
protocol.sendLine(message)
class TTTFactory(Factory):
def __init__(self):
self.state = [0 for x in range(9)]
self.turn = -1
self.users = {} # maps user names to Chat instances
def make_move(self, player, x, y):
if player != self.turn:
return "Not your turn"
i = x + y * 3
if self.state[i] != 0:
return "Invalid move"
self.state[i] = player
# Horrizontal
if self.state[0] == self.state[1] == self.state[2]: return "Win"
if self.state[3] == self.state[4] == self.state[5]: return "Win"
if self.state[6] == self.state[7] == self.state[8]: return "Win"
# Vertical
if self.state[0] == self.state[3] == self.state[6]: return "Win"
if self.state[1] == self.state[4] == self.state[7]: return "Win"
if self.state[2] == self.state[5] == self.state[8]: return "Win"
# Diagonal
if self.state[0] == self.state[4] == self.state[8]: return "Win"
if self.state[6] == self.state[4] == self.state[2]: return "Win"
# Swap turn
self.turn = 0 - self.turn
return "Next move"
def buildProtocol(self, addr):
return TTT(self.users)
# def reactor_runner():
def new_server(conn):
port_num = 8007
conn.send(port_num)
reactor.listenTCP(port_num, TTTFactory())
reactor.run()
I want to have another python program/process send and recieve messages from it. The idea behind the project is create a multiplayer tic tac toe game.
I want to have a server process and 1 or more client processes. For ease of running I'm currently using multiprocessing to run them at the same time. When complete the client process needs to be able to connect over a network as it may not be on the same computer as the host.
Here's a small example of a client capable of connecting to your server above.
from twisted.internet.protocol import ClientFactory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class TTTClientProtocol(LineReceiver):
def lineReceived(self, line):
line = line.strip()
if line == 'You are connected':
self.sendLine(self.factory.username)
else:
print 'SERVER SAYS:', line
class TTTClientFactory(ClientFactory):
protocol = TTTClientProtocol
def __init__(self, name):
self.username = name
name = raw_input('Please enter your name: ')
print 'Connecting...'
reactor.connectTCP('localhost', 8007, TTTClientFactory(name))
reactor.run()
I kept it as simple as I could so you could understand it easily, but to implement the chat part I would need code to read from stdin without blocking the reactor. Since you mentioned you're using a GUI instead of terminal standard input/output, then that is a lot easier actually - just choose a reactor compatible with your GUI library and then use your normal GUI events.
Hope that helps...
I must be missing something in the code. I've rewritten an 'echo server' example to do a bit more when it receives something.
This is how it currently looks:
#!/usr/bin/env python
import select
import socket
import sys
import threading
import time
import Queue
globuser = {}
queue = Queue.Queue()
class Server:
def __init__(self):
self.host = ''
self.port = 2000
self.backlog = 5
self.size = 1024
self.server = None
self.threads = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host,self.port))
self.server.listen(5)
except socket.error, (value,message):
if self.server:
self.server.close()
print "Could not open socket: " + message
sys.exit(1)
def run(self):
self.open_socket()
input = [self.server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == self.server:
# handle the server socket
c = Client(self.server.accept(), queue)
c.start()
self.threads.append(c)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
# close all threads
self.server.close()
for c in self.threads:
c.join()
class Client(threading.Thread):
initialized=0
def __init__(self,(client,address), queue):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
self.queue = queue
self.threads = []
global globuser
print 'Client thread created!'
def run(self):
running = 1
while running:
print 'While running client'
data = self.client.recv(self.size)
print 'Dit we receive data?'
if data:
print 'Data received!'
print 'Fetching data from socket: ',
if data[0] == 'I':
print 'Initializing user: ' + data
user = {'uid': data[1:6] ,'x': data[6:9], 'y': data[9:12]}
globuser[user['uid']] = user
print globuser
initialized=1
m=updateClient(user['uid'], queue)
m.start()
self.threads.append(m)
self.client.send('Beginning - Initialized'+';')
elif data[0] == 'A':
print 'Client has received previous data: ' + data
#On deactivation, nothing works.
self.client.send(data+';')
#print 'Client Data sent: ' + data
else:
print 'Closing'
self.client.close()
running = 0
if self.queue.empty():
print 'Queue is empty'
else:
print 'Queue has information: ',
data2 = self.queue.get(1, 1)
isdata2 = 1
if data2 == 'Exit':
running = 0
print 'Client is being closed'
self.client.close()
if isdata2 == 1:
print 'Sending data to client: ' + data2,
self.client.send(data2)
self.queue.task_done()
isdata2 = 0
time.sleep(1)
class updateClient(threading.Thread):
def __init__(self,uid, queue):
threading.Thread.__init__(self)
self.uid = uid
self.queue = queue
global globuser
print 'updateClient thread started!'
def run(self):
running = 20
test=0
while running > 0:
test = test + 1
self.queue.put('Test Queue Data #' + str(test))
running = running - 1
time.sleep(1)
print 'Updateclient has stopped'
if __name__ == "__main__":
s = Server()
s.run()
This runs fine, although it's kind of silly to keep sending the same data back again along with other data.
In the middle of the code you'll see
#On deactivation, nothing works.
self.client.send(data+';')
#print 'Client Data sent: ' + data
When I DO deactive the self.client.send(data+';') or change it into self.client.send('something else;') it does not work! And the client receives nothing.
Is there something special about the "data" variable? Do I need to format the string in some way?
Here's a cleaned-up, functional version of your code! I tested it myself, though I didn't write unit tests.
There were some syntax errors and other miscellaneous problems with the
original code, so I took some liberties. I'm assuming that the protocol is
framed by using ; as a delimiter, since a ; is sent at the end of every
message to the client, though no framing was being done in the original code.
from twisted.internet import reactor, protocol, task
from twisted.protocols import basic
from twisted.python import log
import sys
class ServerProtocol(basic.LineOnlyReceiver):
delimiter = ';'
def lineReceived(self, line):
if line.startswith('I'):
user = dict(uid=line[1:6], x=line[6:9], y=line[9:12])
self.factory.users[user['uid']] = user
log.msg(repr(self.factory.users))
self.startUpdateClient()
self.sendLine('Beginning - Initialized')
elif line.startswith('A'):
self.sendLine(line)
else:
self.transport.loseConnection()
def _updateClient(self):
if self._running == 0:
self._looper.stop()
return
self._running -= 1
self._test += 1
self.sendLine('Test Queue Data #%d' % (self._test,))
def startUpdateClient(self):
self._running, self._test = 20, 0
self._looper = task.LoopingCall(self._updateClient)
self._looper.start(1, now=False)
class Server(protocol.ServerFactory):
protocol = ServerProtocol
def __init__(self):
self.users = {}
if __name__ == '__main__':
log.startLogging(sys.stderr)
reactor.listenTCP(2000, Server())
reactor.run()