I felt like working on my network programming, threading and OOP skills. I've encountered a problem though.
I have a class named IRC and a class named Pong. I want to use IRC to do stuff like connecting to the server, sending messages, etc. I want to use Pong as a thread in the background which checks for a message containing "PING".
class IRC:
def Connect(self):
try:
HOST = sys.argv[1]
PORT = sys.argv[2]
except IndexError:
print "Usage: "+sys.argv[0]+" [server] [port]\n"
sys.exit(1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
class Pong(threading.Thread):
def Pong(self):
while 1:
IRC.s.send("Test") # IRC has no attribute 's'
Keep in mind that the code above is simplified and only for testing purposes, my question is how I can use variables in one class from another class. The variable s is declared and defined in IRC, but is needed in Pong too.
The interpreter complains that class IRC has no attribute 's' (I've tried calling Connect() first with a sample variable to see if it works, it doesn't).
How do I solve this? I'm new when it comes to threading and object orientation.
Thanks in advance!
You'll need to call an instance of the IRC class which you can pass to the PONG constructor:
class IRC:
def Connect(self):
try:
HOST = sys.argv[1]
PORT = sys.argv[2]
except IndexError:
print "Usage: "+sys.argv[0]+" [server] [port]\n"
sys.exit(1)
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((HOST, PORT))
class Pong(threading.Thread):
def __init__(self,ircclass):
self.myirc = ircclass
def Pong(self):
while 1:
self.myirc.s.send("Test")
gIRC = IRC
gIRC.connect()
myPong = Pong(gIRC)
etc.
Related
I am beginner in programming and trying to develop simple console messenger on Python. I have an idea to extend standard socket.socket object and add to it additional attribute "account_name". I created new class "NamedSoket" based on standard socket class. Here is my code:
class NamedSocket(socket):
def __init__(self, family=-1, type=-1, proto=-1, fileno=None, name=None):
super().__init__(family=-1, type=-1, proto=-1, fileno=None)
self.name = name
def accept(self):
fd, addr = self._accept()
sock = NamedSocket(self.family, self.type, self.proto, fileno=fd, name=self.name)
if getdefaulttimeout() is None and self.gettimeout():
sock.setblocking(True)
return sock, addr
server = NamedSocket()
server.bind(('', 8000))
server.listen()
client = NamedSocket('Bob')
client.connect(('localhost', 8000))
new_client, address = server.accept()
Although new socket objects are created successfully, they do not work properly.. Methods 'recv' and 'send' do not work.. Could you please explain to me, where is the problem?
P.S.: I understand, that my 'idea', maybe, is not good 'at all', but now I became very interested in 'inheritance issue'. From first look, it should work, but it does not...
I've replicated described actions as accurate as possible. This code is working without any error.
import socket as sk
class NamedSocket(sk.socket):
def __init__(self, family=sk.AF_INET, type=sk.SOCK_STREAM, proto=0, fileno=None, name=None):
super().__init__(family, type, proto, fileno)
self.name = name
def accept(self):
fd, addr = self._accept()
sock = NamedSocket(self.family, self.type, self.proto, fileno=fd, name=self.name)
if sk.getdefaulttimeout() is None and self.gettimeout():
sock.setblocking(True)
return sock, addr
server = NamedSocket()
server.bind(('', 8000))
server.listen()
client = NamedSocket(name='Bob')
client.connect(('localhost', 8000))
new_client, address = server.accept()
client.send('hello'.encode())
new_client.recv(1024)
I replaced default parameters in __init()__ method of NamedSocket class to AF_INET, SOCK_STREAM and 0 for the first three arguments. Running such script does not imply any error. You could try do the same changes or edit something related to IP address binded to socket being established on the server side, according to error message.
As you may see, your constructor method takes a bunch of optional parameters:
def __init__(self, family=-1, type=-1, proto=-1, fileno=None, name=None):
super().__init__(family=-1, type=-1, proto=-1, fileno=None)
self.name = name
When you try to create an object of class NamedSocket for client, you pass the only 'Bob' parameter, which, by precedence of function argument, will be passed to family argument, but not to name. For doing things in the key you want, you may write:
client = NamedSocket(name='Bob')
First of all I need to say I've never tried coding in python before...
I'm trying to make a Twitch IRC bot working but I keep failing...
My bot.py code looks like this:
from src.lib import irc as irc_
from src.lib import functions_general
from src.lib import functions_commands as commands
from src.config import config
class PartyMachine:
def __init__(self, config):
self.config = config
self.irc = irc_.irc(config)
self.socket = self.irc.get_irc_socket_object()
def sock(self):
irc = self.irc
sock = self.socket
config = self.config
kage = sock
while True:
data = sock.recv(2048).rstrip()
if len(data) == 0:
pp('Connection was lost, reconnecting.')
sock = self.irc.get_irc_socket_object()
if config['debug']:
print (data)
my config.py is here:
'socket_buffer_size': 1024
My irc.py is here:
def get_irc_socket_object(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
self.sock = sock
try:
sock.connect((self.config['server'], self.config['port']))
except:
pp('Cannot connect to server (%s:%s).' % (self.config['server'], self.config['port']), 'error')
sys.exit()
sock.settimeout(None)
def sock_send(sock, send, self):
sock.send('USER %s\r\n' % self.config['username'], sock.encode('utf-8'), send.encode('utf-8'))
sock.send('PASS %s\r\n' % self.config['oauth_password'])
sock.send('NICK %s\r\n' % self.config['username'])
if self.check_login_status(sock.recv(1024)):
pp('Login successful.')
else:
pp('Login unsuccessful. (hint: make sure your oauth token is set in self.config/self.config.py).', 'error')
sys.exit()
and my serve.py is here:
from sys import argv
from src.bot import *
from src.config.config import *
bot = PartyMachine(config).sock()
It keeps failing with "AttributeError 'nonetype' object has no attribute 'recv'". How can this be ?
Your get_irc_socket_object(self) might be the problem. You call it with the line self.socket = self.irc.get_irc_socket_object(). This means that python expects the function get_irc_socket_object(self) to return a socket object, but you don't return anything (you just write self.sock = sock, which doesn't do anything because you use self.socket for the rest of your code). As a result, the function returns none, so now self.socket just has that as its value. Therefore, when you make the call to recv you get your error
Also, please clean up your variable names. Sock is used way too often in your code and makes it very hard to follow.
I made a class in python that has a socket in it. When I try to run multiple instances of the same class I get this error:
error: [Errno 10056] A connect request was made on an already connected socket
I can see what the error is saying, but I though the classes were independent of each other when they run. So it wouldn't interfere.
Here's the code I'm using:
class Bot():
HOST = "localhost"
PORT = 6667
s = socket.socket()
def Connect(self):
self.s.connect((self.HOST, self.PORT))
Then when I create the bots:
bots = []
def Setup_Bot():
global bots
_bot = Bot()
_bot.Connect()
bots.append(_bot)
if __name__ == "__main__":
for i in range(5):
Setup_Bot()
sleep(1)
print "Done Setting Up"
How would I be able to get this to work?
Make the socket s an instance variable instead of setting it on the class. All your Bot instances now share the same class attributes and thus, the same socket.
class Bot():
HOST = "localhost"
PORT = 6667
def __init__(self):
self.s = socket.socket()
def Connect(self):
self.s.connect((self.HOST, self.PORT))
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)
I'm trying to create a small program that will log information output from a device via TCP
Basically this just streams data out, that i want to capture, and dump into a database for dealing with later
but the device reboots so i need to be able to reconnect when the socket closes with out any human interference
so this is what i have so far
import socket, time, logging, sys, smtplib # Import socket module
logging.basicConfig(filename='Tcplogger.log',level=logging.DEBUG,format='%(asctime)s : %(levelname)s : %(message)s')
logging.info('|--------------------------------------|')
logging.info('|--------------- TCP Logger Starting---|')
logging.info('|--------------------------------------|')
host = '127.0.0.01' # host or Ip address
port = 12345 # output port
retrytime = 1 # reconnect time
reconnectattemps = 10 # Number of time to try and reconnect
class TPCLogger:
def __init__(self):
logging.debug('****Trying connection****')
print('****Trying connection****')
self.initConnection()
def initConnection(self):
s = socket.socket()
try:
s.connect((host, port))
logging.debug('****Connected****')
except IOError as e:
while 1:
reconnectcount = 0;
logging.error(format(e.errno)+' : '+format(e.strerror))
while 1:
reconnectcount = reconnectcount + 1
logging.error('Retrying connection to Mitel attempt : '+str(reconnectcount))
try:
s.connect((host, port))
connected = True
logging.debug('****Connected****')
except IOError as e:
connected = False
logging.error(format(e.errno)+' : '+format(e.strerror))
if reconnectcount == reconnectattemps:
logging.error('******####### Max Reconnect attempts reached logger will Terminate ######******')
sys.exit("could Not connect")
time.sleep(retrytime)
if connected == True:
break
break
while 1:
s.recv(1034)
LOGGER= TCPLogger()
Which all works fine on start up if a try to connect and its not there it will retry the amount of times set by reconnectattemps
but he is my issue
while 1:
s.recv(1034)
when this fails i want to try to reconnect
i could of course type out or just copy my connection part again but what i want to be able todo is call a function that will handle the connection and retry and hand me back the connection object
for example like this
class tcpclient
#set some var
host, port etc....
def initconnection:
connect to socket and retry if needed
RETURN SOCKET
def dealwithdata:
initconnection()
while 1:
try:
s.recv
do stuff here copy to db
except:
log error
initconnection()
I think this is possible but im really not geting how the class/method system works in python so i think im missing something here
FYI just in case you didn't notice iv very new to python. any other comments on what i already have are welcome too :)
Thanks
Aj
Recommendation
For this use-case I would recommend something higher-level than sockets. Why? Controlling all these exceptions and errors for yourself can be irritating when you just want to retrieve or send data and maintain connection.
Of course you can achieve what you want with your plain solution, but you mess with code a bit more, methinks. Anyway it'll look similarly to class amustafa wrote, with handling socket errors to close/reconnect method, etc.
Example
I made some example for easier solution using asyncore module:
import asyncore
import socket
from time import sleep
class Client(asyncore.dispatcher_with_send):
def __init__(self, host, port, tries_max=5, tries_delay=2):
asyncore.dispatcher.__init__(self)
self.host, self.port = host, port
self.tries_max = tries_max
self.tries_done = 0
self.tries_delay = tries_delay
self.end = False # Flag that indicates whether socket should reconnect or quit.
self.out_buffer = '' # Buffer for sending.
self.reconnect() # Initial connection.
def reconnect(self):
if self.tries_done == self.tries_max:
self.end = True
return
print 'Trying connecting in {} sec...'.format(self.tries_delay)
sleep(self.tries_delay)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.connect((self.host, self.port))
except socket.error:
pass
if not self.connected:
self.tries_done += 1
print 'Could not connect for {} time(s).'.format(self.tries_done)
def handle_connect(self):
self.tries_done = 0
print 'We connected and can get the stuff done!'
def handle_read(self):
data = self.recv(1024)
if not data:
return
# Check for terminator. Can be any action instead of this clause.
if 'END' in data:
self.end = True # Everything went good. Shutdown.
else:
print data # Store to DB or other thing.
def handle_close(self):
print 'Connection closed.'
self.close()
if not self.end:
self.reconnect()
Client('localhost', 6666)
asyncore.loop(timeout=1)
reconnnect() method is somehow core of your case - it's called when connection is needed to be made: when class initializes or connection brokes.
handle_read() operates any recieved data, here you log it or something.
You can even send data using buffer (self.out_buffer += 'message'), which will remain untouched after reconnection, so class will resume sending when connected again.
Setting self.end to True will inform class to quit when possible.
asyncore takes care of exceptions and calls handle_close() when such events occur, which is convenient way of dealing with connection failures.
You should look at the python documentation to understand how classes and methods work. The biggest difference between python methods and methods in most other languages is the addition of the "self" tag. The self represents the instance that a method is called against and is automatically fed in by the python system. So:
class TCPClient():
def __init__(self, host, port, retryAttempts=10 ):
#this is the constructor that takes in host and port. retryAttempts is given
# a default value but can also be fed in.
self.host = host
self.port = port
self.retryAttempts = retryAttempts
self.socket = None
def connect(self, attempt=0):
if attempts<self.retryAttempts:
#put connecting code here
if connectionFailed:
self.connect(attempt+1)
def diconnectSocket(self):
#perform all breakdown operations
...
self.socket = None
def sendDataToDB(self, data):
#send data to db
def readData(self):
#read data here
while True:
if self.socket is None:
self.connect()
...
Just make sure you properly disconnect the socket and set it to None.