Run Socket Script on Multiple Ports - python

What I want to do is run the following script on every port, 1025+. What I am doing is making a Blackjack iPhone app that interacts with this script for online gaming. The thing is, I would want to put this on each port manually by changing the port to listen each time for all the ports. How can I do it so that there is a new table on every port. Each table has an ID the app will check for to see the amount of players and who is at the table.
The socket sets the ID for the Table class, but I need to be on multiple ports to be able to keep that table going and saving every player moves and such.
Bottom line, how can I make this script run on every port, how can I change the listening port by itself, and how can I have python make all Tables by itself on each port?
class Table:
def __init__(self, id):
self.players = []
self.positions = {'1': '', '2': '', '3': '', '4': ''}
def sit(self, player_id, position):
self.positions[position] = player_id
# --------------------------------------------- #
# --------------------------------------------- #
class Socket(Protocol):
def connectionMade(self):
#self.transport.write("""connected""")
self.factory.clients.append(self)
print "Clients are ", self.factory.clients
def connectionLost(self, reason):
self.factory.clients.remove(self)
def dataReceived(self, data):
#print "data is ", data
a = data.split(':')
if len(a) > 1:
command = a[0]
content = a[1]
msg = ""
print msg
for c in self.factory.clients:
c.message(msg)
def message(self, message):
self.transport.write(message)
factory = Factory()
factory.protocol = Socket
factory.clients = []
reactor.listenTCP(1025, factory)
print "Blackjack server started"
reactor.run()

Answering your question
You ask:
How can I make this script run on every port?
How can I change the listening port by itself?
How can I have python make all Tables by itself on each port?
I think the answer here is to simply use a loop to bind the factory to as many ports as you want.
However, since you're storing a list of clients in your factory as well, you'll need to create a new factory for reach port, as well. So something like:
factories = [ ]
for i in range(0, NUM_TABLES):
factory = Factory()
factory.protocol = Socket()
factory.clicents = []
factories.append(factory)
reactor.listenTCP(1025 + i, factory)
reactor.run()
You're using classes, so each factory keeps its own list of clients, each one has its own Socket instance to manage the connection. You don't show how Table instances are instantiated, but as long as each Socket or Factory instance instantiates and maintains a reference to the Table, this should allow you to have multiple connections, each with its own state.
By keeping a list of all factories, you can iterate over them to make a list of running games, etc.
Considering a different architecture
While the above might work, it's not how client-server systems are typically architected.
In particular, your system here requires your client to know what port to use. That may work ad hoc when you're all in your living room together, but it's tedious and won't scale in general.
What you want is something, like a webserver, that listens on one port to establish the connection, and then tells the client: "hey, your table id is 25, use that whenever you want to talk". In addition, this means making a list of tables available to the client, so they can pick one. And, you can get fancier from there: a special expiring cookie given to client so that it doesn't accidentally hack/disturb a game that it's no longer part of, etc.

Related

Python Twisted best way to signal events to a proxy

I will be hosting a service that will be acting somewhat like a proxy for something I am a client to.
So I want my ProxyService (a twisted.protocol server) to takes lots of actors (clients). On the server side of things, I need a global connection (only need 1 connection to it for all clients) to an ExistingService (code I didn't write, and I'm a client to it).
When the ExistingService says something interesting, I need to broadcast it to all actors.
When an actor says something to my ProxyService, I need to check if it looks good to me. If it does, I need to inform the ExistingService.
I think I know how to solve this using global variables, but just wondering if better way to push the messages.
You have the basic design well established.
It's a basic "man in the middle" approach.
There are many ways to implement it, but this should get you started:
from twisted.internet import endpoints, protocol, reactor
class ProxyClient(protocol.Protocol):
def connectionMade(self):
print('[x] proxy connection made to server')
self.factory.proxy_proto = self
def connectionLost(self, reason):
print('[ ] proxy connection to server lost: {0}'.format(reason))
self.factory.proxy_proto = None
def dataReceived(self, data):
print('==> received {0} from server'.format(data))
print('<== transmitting data to all actors')
for actor in self.factory.actors:
actor.transport.write(data)
class Actor(protocol.Protocol):
def connectionMade(self):
print('[x] actor connection established')
self.factory.actors.add(self)
def connectionLost(self, reason):
print('[ ] actor disconnected: {0}'.format(reason))
self.factory.actors.remove(self)
def dataReceived(self, data):
print('==> received {0} from actor'.format(data))
proxy_connection = self.factory.proxy_factory.proxy_proto
if proxy_connection is not None:
print('<== transmitting data to server through the proxy')
proxy_connection.transport.write(data)
else:
print('[ ] proxy connection to server has not been established')
def setup_servers():
PROXY_HOST = '127.0.0.1'
PROXY_PORT = 9000
proxy_factory = protocol.ClientFactory()
proxy_factory.protocol = ProxyClient
proxy_factory.proxy_proto = None
proxy_factory.actors = set()
proxy_client = endpoints.TCP4ClientEndpoint(reactor, port=PROXY_PORT, host=PROXY_HOST)
proxy_client.connect(proxy_factory)
ACTOR_HOST = '127.0.0.1'
ACTOR_PORT = 8000
actor_factory = protocol.Factory()
actor_factory.protocol = Actor
actor_factory.proxy_factory = proxy_factory
actor_factory.actors = proxy_factory.actors
actor_server = endpoints.TCP4ServerEndpoint(reactor, port=ACTOR_PORT, interface=ACTOR_HOST)
actor_server.listen(actor_factory)
def main():
setup_servers()
reactor.run()
main()
The core logic that allows the data received from the server to be proxied to actors is proxy_factory.actors = set() and actor_factory.actors = proxy_factory.actors.
Most "list-like" containers, for lack of better words, are "global" and this example gives context into each connection's factory objects.
When an actor connects to the server, an Actor protocol is appended to the set and when data is received, each protocol in the set will get the data.
See the respective dataReceived() methods of each protocol object on how that works.
The example above doesn't use global variables at all, but that's not to say that you couldn't use them.
See how far you can get using this method of passing around variables that give context into other objects.
Also, certain situations weren't explicitly handled, such as caching received data in the event the server or actors haven't connected yet.
Hopefully there's enough information here for you to determine the best course of action based on your needs.
There's some room for streamlining the syntax to make it shorter as well.
As a side note. An alternative to global variables is picobox. It's a dependency injector library but I've found that it satisfies most my needs when I require parameters from external sources.

Django & Socket Server: Can I add socket server instance to "memory"?

I'm working with:
Django 1.11
Python Sockets
I have a Socket server like this:
class SocketServer(threading.Thread):
def __init__(self, ip="127.0.0.1", port=5000, _buffer=1024):
super(RoomSocketServer, self).__init__()
self.IP = ip
self.PORT = port
self.RECV_BUFFER = _buffer # Advisable to keep it as an exponent of 2
self.CONNECTION_LIST = [] # list of socket clients
self.MESSAGE_QUEUES = {} # List of message queue by socket
self.OUTPUTS = []
self.SERVER = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# this has no effect, why ?
self.SERVER.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.SERVER.bind((self.IP, self.PORT))
self.SERVER.listen(10)
# Add server socket to the list of readable connections
self.CONNECTION_LIST.append(self.SERVER)
self.ROOM = Room.objects.create(port=port, ip=ip, type_room=type_room)
def read_sockets(self, read_sockets):
''' ... '''
def write_sockets(self, write_sockets):
''' ... '''
def error_sockets(self, error_sockets):
''' ... '''
def run(self):
while 1:
# Get the list sockets which are ready to be read through select
read_sockets, write_sockets, error_sockets = select.select(self.CONNECTION_LIST, self.OUTPUTS, [])
# Read sockets
self.read_sockets(read_sockets)
self.write_sockets(write_sockets)
self.error_sockets(error_sockets)
self.SERVER.close()
I can run this SocketServer like this anywhere on Django (custom_command, a view, celery...):
from socket_server import SocketServer
socket_server = SocketServer()
socket_server.start()
# And the code continues while the socket server is running
# I would like to save socket_server instance anywhere to access
# Later from anywhere or trigger a signal to finish it
Like I say above, I would like to know (if possible) where would any of you save the instance of the server to access it from different parts of the Django project ?
UPDATE
I tried using memcached on Django but when I try to store the SocketServer instance on memcached I get this error:
PicklingError: Can't pickle : attribute lookup thread.lock failed
The answer is yes and its simple
serverlst = dict()
for x in range(0,5):
tmp = SocketServer("",5000+x) # Note that you should change the port because each port can be listened only by one socket server
tmp.start()
serverlst[5000+x]=tmp
or you can access to server socket with
serverlst[5000].getName() # returns the server socket with port 5000's thread
when you print serverlst after loop, you can see this
{5000: <SocketServer(Thread-5, started 139741574940416)>, 5001: <SocketServer(Thread-6, started 139741583333120)>, 5002: <SocketServer(Thread-7, started 139741658834688)>, 5003: <SocketServer(Thread-8, started 139741667227392)>, 5004: <SocketServer(Thread-9, started 139741233895168)>}
or you can add it to list()
update
sorry i didn't see
I would like to know (if possible) where would any of you save the instance of the server to access it from different parts of the Django project
i can say it depends on the way you develop and i think its not a good idea. its better to use a bridge between threads and Django project like redis which allows you to keep data and access it from entire system and you can use python redis client.

Cant receive data from socket

I'm making a client-server program, and there is problem with client part.
Problem is in infinite receiving data. I've tested this particular class, listed below, in a python interpreter. I've succesfuly(maybe not) connected to google, but then program stoped in function recvData() in data = self.socket.recv(1024)
class client():
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = self.connect()
self.command = commands()
def connect(self):
'''
Connect to a remote host.
'''
try:
import socket
return socket.create_connection((self.host, self.port))
except socket.error:
print(":: Failed to connect to a remote port : ")
def sendCommand(self, comm):
'''
Send command to remote host
Returns server output
'''
comman = comm.encode()
# for case in switch(comman):
# if case(self.command.RETRV_FILES_LIST.encode()):
# self.socket.send(b'1')
# return self.recvData()
# if case():
# print(":: Got wrong command")
if (comman == b'1'):
self.socket.send(b'1')
return self.recvData()
def recvData(self):
'''
Receives all the data
'''
i = 0
total_data = []
while(True):
data = self.socket.recv(1024)
if not data: break
total_data.append(data)
i += 1
if i > 9:
break
return total_data
about commented part :
I thought problem in Case realization, so used just if-then statement. But it's not.
Your problem is that self.socket.recv(1024) only returns an empty string when the socket has been shut down on the server side and all data has been received. The way you coded your client, it has no idea that the full message has been received and waits for more. How you deal with the problem depends very much on the protocol used by the server.
Consider a web server. It sends a line-delimited header including a content-length parameter telling the client exactly how many bytes it should read. The client scans for newlines until the header is complete and then uses that value to do recv(exact_size) (if large, it can read chunks instead) so that the recv won't block when the last byte comes in.
Even then, there a decisions to make. The client knows how large the web page is but may want to send a partial data to the caller so it can start painting the page before all the data is received. Of course, the caller needs to know that is what happens - there is a protocol or set of rules for the API itself.
You need to define how the client knows a message is complete and what exactly it passes back to its caller. A great way to deal with the problem is to let some other protocol such as [zeromq](http://zeromq.org/ do the work for you. A simple python client / server can be implemented with xmlrpc. And there are many other ways.
You said you are implementing a client/server program then you mentioned "connected to google" and telnet... These are all very different things and a single client strategy won't work with all of them.

How to deal with multiple serial ports for R/W using twisted?

Going through the twisted finger tutorial and seen the SO questions:
Question-1
Question-2
However, I can't (yet) write a twisted program that can read & write from multiple serial ports, especially where the protocol involves reading single or multiple lines, and writing back to the device accordingly.
What I am trying to do is open 2 pairs (i.e. total of 4) serial ports, for 2 modems. Communication with modems is using Hayes AT command set. While most of the command/response exchanges with modem is via the command-port, there are few diagnostic information that are available only via the diagnostic-port, for each modem. The diagnostic information should lead to a state-machine (device-state, connection-state) to be modified.
Here is a rough skeletal program of what I understand as the potential approach (based on single port examples):
class CommandProtocol(LineOnlyReceiver):
def connectionMade(self):
log.msg("Connected to command port")
def lineReceived(self, line):
print repr(line)
processCommandLine(line)
class DiagnosticProtocol(LineOnlyReceiver):
def connectionMade(self):
log.msg("Connected to diag port")
def lineReceived(self, line):
print repr(line)
processDiagnosticLine(line)
...
# modem1 ports
cmdPort[0] = SerialPort(CommandProtocol, "/dev/ttyUSB0", reactor, 115200)
diagPort[0] = SerialPort(DiagnosticProtocol, "/dev/ttyUSB1", reactor, 115200)
# modem2 ports
cmdPort[1] = SerialPort(CommandProtocol, "/dev/ttyUSB3", reactor, 115200)
diagPort[1] = SerialPort(DiagnosticProtocol, "/dev/ttyUSB4", reactor, 115200)
However, I am at loss, as to how do I do the following:
How/where do I accept CLI input from user, that then triggers sending a set of AT command to the modems ?
Correlate the information received on command port for ttyUSB0 & ttyUSB1 for modem1, and similarly for the other pair for modem2 ? Note that each modem has it's own state-machine (device-state and connection-state)
Does twisted provide any mechanism for management of multiple state-machines by application ?
It is possible that the USB-serial connection to the modem is destroyed due to the modem being unplugged, and re-established on being plugged back-in. How can I detect such events and add the monitoring of the corresponding device-ports to the reactor ? Currently, I'm doing it statically in the main application.
Note on your example code
I don't see you instantiating your classes before registering them to the reactor. I expect that will fail badly. Here is a similar snippet of running code of mine:
# stuff to process messages coming from the serial port
class SerialEater(basic.LineReceiver):
statusCallback = None
def __init__(self):
self.keyinprocess = None
def lineReceived(self, data):
self.dealWithSerial(data)
def connectionLost(self, reason):
if(reactor.running):
print "Serial lost but reactor still running! reason: " + str(reason) + " at time " + time.asctime()
[...etc...]
# Register the serialport into twisted
serialhandler = SerialEater() # <------------- instantiate
SerialPort(serialhandler, '/dev/ttyUSB0', reactor, baudrate=115200)
How/where do I accept CLI input from user, that then triggers sending a set of AT command to the modems ?
Much like how you can register Serial handlers into Twisted, you can register handlers for standard io, for instance:
# stuff to pull cbreak char input from stdin
class KeyEater(basic.LineReceiver):
def __init__(self):
self.setRawMode() # Switch from line mode to "however much I got" mode
def connectionLost(self, reason):
if(reactor.running):
self.sendLine( "Keyboard lost but reactor still running! reason: " + str(reason) + " at time " + time.asctime())
def rawDataReceived(self, data):
key = str(data).lower()[0]
try:
if key == '?':
key = "help"
[...etc...]
# register the stdio handler into twisted
keyboardobj = KeyEater()
keyboardobj.serialobj = serialhandler
stdio.StandardIO(keyboardobj,sys.stdin.fileno())
Correlate the information received on command port for ttyUSB0 & ttyUSB1 for modem1, and similarly for the other pair for modem2 ? Note that each modem has it's own state-machine (device-state and connection-state)
In normal use, each connection-instance is going to have its own state machine (wrapped up in the instance of the class that you register into the reactor along with the connection).
You as the programmer choose how you want to connect the states of the classes, but often its via pushing reference to the partner classes.
Below, this answer contains runnable code that will illustrate how data is connected between state-machines/interface. This is also illustrated in this SO: Persistent connection in twisted
Does twisted provide any mechanism for management of multiple state-machines by application ?
If by "application" you mean "your twisted code" then then the answer is absolutely YES!
The typical Twisted app is an array of state-machines, all with some amazingly well defined interfaces. I started my Twisted adventure intending to write an app with two state-machines (a serial and keyboard), but when I became comfortable with twisted was doing I realized it was trivial to add on extra interfaces and state-machines (through all the wonder of the tx libraries). All in one afternoon I added on a rough web interface, a websocket interface, then laid SSL over both and even added on an SSH debug interface. Once you get a rolling, adding interfaces and state-machines become trivial.
In many (all?) cases, the twisted model is that a state-machine will reside in an instantiated class that is tied to a connection and that has been registered into the (one-and-only-one) main event-loop.
With connection types that spawn off new state-machines (think http connections) you register one factory-class/state-machine along with the listening connection which together enable the app of spawning off new classes/state-machines for each new connection. Twisted applications routinely 10s or even 100s of thousands of concurrent instances of state when run at scale.
Twisted is amazing if your trying to glue together different protocols and states (... with all of it being in a event loop of your choice (select/epoll/kqueue/etc))
The following is runnable sample code that should illustrate many of these points. Read the comments before def main() for more background on the code:
#!/usr/bin/python
#
# Frankenstein-esk amalgam of example code
# Key of which comes from the Twisted "Chat" example
# (such as: http://twistedmatrix.com/documents/12.0.0/core/examples/chatserver.py)
import sys # so I can get at stdin
import os # for isatty
import termios, tty # access to posix IO settings
from random import random
from twisted.internet import reactor
from twisted.internet import stdio # the stdio equiv of listenXXX
from twisted.protocols import basic # for lineReceiver for keyboard
from twisted.internet.protocol import Protocol, ServerFactory
class MyClientConnections(basic.LineReceiver):
def __init__(self):
self.storedState = "Idle"
self.connectionpos = None
def connectionMade(self):
self.factory.clients.append(self) # <--- magic here :
# protocol automagically has a link to its factory class, and
# in this case that is being used to push each new connection
# (which is in the form of this class) into a list that the
# factory can then access to get at each of the connections
self.connectionpos = str(self.factory.clients.index(self)) # figure out
# where I am in the connection array
print "Got new client! (index:", self.connectionpos + ")"
self.transport.write("---\nYour connection: " + self.connectionpos + "\n---\n")
def connectionLost(self, reason):
print "Lost a client!"
self.factory.clients.remove(self)
# used to pretend that something was typed on a telnet connection
def fakeInput(self, message):
self.transport.write("FAKING Input: '" + message + "'\n")
self.lineReceived(message)
#this is only in a def on its own so I can lump my demo callLater
def stateUpdate(self, newState, delay):
self.storedState = newState
# the following is a hack to fake data coming in this interface
reactor.callLater(delay, self.fakeInput, newState + " DONE")
def processInput(self, newState):
# all the logic in here is junk to make a demo, real code may or may-not look like
# this. This junk logic is an example statemachine though
if self.storedState == "Idle":
if newState == "start":
self.stateUpdate("State A", 1)
# send a message to this connection
self.transport.write("starting state machine\n")
# send a message to the term in which the script it running
print "Connection [" + self.connectionpos + "] starting state machine"
elif self.storedState == "State A":
if newState == "State A DONE":
self.transport.write("Beginning state B\n")
self.stateUpdate("State B", 2)
elif self.storedState == "State B":
if newState == "State B DONE":
self.transport.write("Beginning state C\n")
self.stateUpdate("State C", 2)
elif self.storedState == "State C":
if newState == "State C DONE":
self.storedState = "Idle"
# send a message to this connection
self.transport.write("Returning to Idle state\n")
# send a message to the term in which the script it running
print "Connection [" + self.connectionpos + "] return to Idle state"
def lineReceived(self, line):
# print "received '" + line +"' from connection", self.factory.clients.index(self)
self.processInput(line)
class MyServerFactory(ServerFactory):
protocol = MyClientConnections
def __init__(self):
self.clients = [] # this gets filled from the class above
def sendToAll(self, message):
for c in self.clients: # Read MyClientConnections class for background
c.transport.write(message)
def randStart(self, width):
for c in self.clients:
startDelay = random() * width
print "Starting client " + str(c.connectionpos) + " in " +str(startDelay) + " secs"
reactor.callLater(startDelay, c.processInput, "start")
# to set keyboard into cbreak mode -- just because I like it that way...
class Cbreaktty(object):
org_termio = None
my_termio = None
def __init__(self, ttyfd):
if(os.isatty(ttyfd)):
self.org_termio = (ttyfd, termios.tcgetattr(ttyfd))
tty.setcbreak(ttyfd)
print ' Set cbreak mode'
self.my_termio = (ttyfd, termios.tcgetattr(ttyfd))
else:
raise IOError #Not something I can set cbreak on!
def retToOrgState(self):
(tty, org) = self.org_termio
print ' Restoring terminal settings'
termios.tcsetattr(tty, termios.TCSANOW, org)
class KeyEater(basic.LineReceiver):
def __init__(self, factoryObj):
self.setRawMode() # Switch from line mode to "however much I got" mode
# the following is one of the key connecting ideas in twisted, the object
# that contains another state machine (really all of the tcp statemachines)
# has been passed into this class via its init.
self.factoryObj = factoryObj
def rawDataReceived(self, data):
key = str(data).lower()[0]
if key == 's':
# The following line is going to call (from within the factory object)
# the random start def
self.factoryObj.randStart(5)
elif key == 'd':
print "State Dump of connections"
print "-------------------------"
for c in self.factoryObj.clients:
print "#" + str(c.connectionpos) + " " + c.storedState
elif key == 'q':
reactor.stop()
else:
print "--------------"
print " If you haven't already, connect to this script via a"
print " 'telnet localhost 5000' at least one (multiple connections"
print " are better)"
print "Press:"
print " s - randomly start all clients"
print " d - dump the state of all connected clients"
print " q - to cleanly shutdown"
print " Note: you can type commands in the connections, things"
print " most useful of which is 'start'"
print "---------------"
# Custom tailored example for SO:30397425
#
# This code is a mishmash of styles and techniques. Both to provide different examples of how
# something can be done and because I'm lazy. Its been built and tested on OSX and linux,
# it should be portable (other then perhaps termal cbreak mode). If you want to ask
# questions about this code contact me directly via mail to mike at partialmesh.com
#
# While it isn't directly using serial ports, the tcp connections that its using are a good
# parallel.
#
# It should be used by running the script and then opening up many windows telnet'ing into
# localhost 5000.
#
# Once running press any key in the window where the script was run and it will give
# instructions.
# The normal use case would be to type "s" to queue statemachine
# start-ups, then repeatedly press 'd' to dump the status of all the state machines
#
# 'start' can be typed into any of the telnet connections to start them by hand too.
def main():
client_connection_factory = MyServerFactory()
try:
termstate = Cbreaktty(sys.stdin.fileno())
except IOError:
sys.stderr.write("Error: " + sys.argv[0] + " only for use on interactive ttys\n")
sys.exit(1)
keyboardobj = KeyEater(client_connection_factory)
stdio.StandardIO(keyboardobj,sys.stdin.fileno())
reactor.listenTCP(5000, client_connection_factory)
reactor.run()
termstate.retToOrgState()
if __name__ == '__main__':
main()
It is possible that the USB-serial connection to the modem is destroyed due to the modem being unplugged, and re-established on being plugged back-in. How can I detect such events and add the monitoring of the corresponding device-ports to the reactor ? Currently, I'm doing it statically in the main application.
After research I don't have an easy answer. I still suspect that the following logic will be close to a solution but I didn't have any luck finding code that implements this today.
My guess is there will be a reasonable way to figure out if a USB event has occurred, and work out if a serial device has been added. But I doubt there will a good way to figure out if it is one of your serial devices - much less if its your Command or Diagnostic interface (Unless your building hardware and can control the USB IDs of the devices)
Events are fired on errors with serial ports (at least from my experience on linux), but I'm unsure how/where a USB unplug would register.
Other links that might be of use to you
Twisted implementation of Utilities for talking to a GSM modem over
USB via AT commands: https://github.com/smn/txgsm
Twisted implementation of weather station through USB:
https://gist.github.com/claws/2464017

python asyncore server send data to only one sock

I have to send data only to a connection, as I can do?
server:
import asyncore, socket, threading
class EchoHandler(asyncore.dispatcher_with_send):
def __init__(self,sock):
asyncore.dispatcher.__init__(self,sock=sock);
self.out_buffer = ''
def handle_read(self):
datos = self.recv(1024);
if datos:
print(datos);
self.sock[0].send("signal");
class Server(asyncore.dispatcher):
def __init__(self,host='',port=6666):
asyncore.dispatcher.__init__(self);
self.create_socket(socket.AF_INET, socket.SOCK_STREAM);
self.set_reuse_addr();
self.bind((host,port));
self.listen(1);
def handle_accept(self):
self.sock,self.addr = self.accept();
if self.addr:
print self.addr[0];
handler = EchoHandler(self.sock);
def handle_close(self):
self.close();
cliente = Server();
asyncore.loop()
this line is an example fails, but I want to send data to zero sock:
self.sock[0].send("probando");
for example, if I have 5 sockets choose who to send the data
Explanation
You tried to get sock from list and execute its send method. This causes error, because EchoHandler neither has sock attribute nor it's a list of sockets. The right method is to get instance of EchoHandler you want (based on, eg. IP address, or slots assigned by some user-defined protocol) and then use its send method - here (with dispatcher_with_send) its also better to use special buffer for that than send.
EchoHandler instantion is created on every accept of connection - from then it is an established channel for communication with the given host. Server listens for any non-established connection, while EchoHandlers use socks (given by Server in handle_accept) for established ones, so there are as many EchoHandler instances as connections.
Solution
You need to make some list of connections (EchoHandler instantions; we'll use buffer, not socket's send() directly) and give them opportunity to delete their entries on close:
class Server(asyncore.dispatcher):
def __init__(self, host='', port=6666):
...
self.connections = []
def handle_accept(self):
...
handler = EchoHandler(self.sock, self);
self.connections.append(self.sock)
...
def remove_channel(self, sock):
if sock in self.connections:
self.connections.remove(sock)
class EchoHandler(asyncore.dispatcher_with_send):
def __init__(self, sock, server):
...
self.server = server
def handle_read(self):
datos = self.recv(1024);
if datos:
print(datos);
self.out_buffer += 'I echo you: ' + datos
def handle_close(self):
self.server.remove_channel(self)
self.close()
EchoHandler is now aware of server instance and can remove its socket from list. This echo example is now fully functional, and with working socket list we can proceed to asynchronous sending.
But, at this point you can use this list as you wanted - cliente.connections[0].out_buffer += 'I am data' will do the work, but probably you'd want some better controlling of this. If yes, go ahead.
'For whom, by me'
In order to send data asynchronously, we need to separate asyncore from our control thread, in which we'll enter what to send and to whom.
class ServerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True # if thread is a daemon, it'll be killed when main program exits
self.cliente = Server()
self.start()
def run(self):
print 'Starting server thread...'
asyncore.loop()
thread = ServerThread()
while True:
msg = raw_input('Enter IP and message divided by semicolon: ')
if msg == 'exit':
break
ip, data = msg.split('; ')
for sock in thread.cliente.connections:
if sock.addr[0] == ip:
sock.out_buffer += data
break
This will work and wait for destination IP and data. Remember to have client connected.
As I said, you can use anything to indicate which socket is which. It can be a class with fields for eg. IP and username, so you could send data only to peers whose usernames start with 'D'.
But...
This solution is a bit rough and needs better knowledge of asyncore module if you want to send data nicely (here it has some delay due to how select() works) and make good use of this socket wrapper.
Here and here are some resources.
Syntax note
Although your code will now work, your code has some not-nice things. Semicolons on instructions ends don't cause errors, but making nearly every variable of class attribute can lead to them. For example here:
def handle_accept(self):
self.sock,self.addr = self.accept();
if self.addr:
print self.addr[0];
handler = EchoHandler(self.sock);
self.sock and self.addr might be used in that class for something other (eg. socket-related thing; addresses) and overriding them could make trouble. Methods used for requests should never save state of previous actions.
I hope Python will be good enough for you to stay with it!
Edit: sock.addr[0] can be used instead of sock.socket.getpeername()[0] but it requires self.addr not to be modified, so handle_accept() should look like this:
def handle_accept(self):
sock, addr = self.accept()
if addr:
print addr[0]
handler = EchoHandler(sock, self)
self.connections.append(handler)

Categories