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')
Related
I'm trying to write a simple TCP socket server.
I'm getting this error:
File "patch", line 9, in __init__
self.__SocketServer__.listen(1)
AttributeError: 'NoneType' object has no attribute 'listen'
And I can't understand why.
Code:
class DataManager:
def __init__(self):
self.__port__ = 2121
self.__ip__ = "127.0.0.1"
self.__SocketServer__ = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__SocketServer__ = self.__SocketServer__.bind((self.__ip__, self.__port__))
self.__SocketServer__.listen(1)
self.__log__("Waiting car connection...")
self.__CarSocket__, addr = self.__SocketServer__.accept()
self.__log__("Car socket connected")
self.__CarSocket__ = None
socket.bind() returns None. Don't assign this back to your socket attribute:
self.__SocketServer__ = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__SocketServer__.bind((self.__ip__, self.__port__))
self.__SocketServer__.listen(1)
On a side note: don't use __doubleunderscore__ names for your own projects. Python reserves this class of names for its own use.
If you want to mark names as internal to the instance, use single underscores at the start of names. Python classes have no privacy model, all attributes are always accessible from the outside.
So this suffices:
class DataManager:
def __init__(self):
self._port = 2121
self._ip = "127.0.0.1"
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.bind((self._ip, self._port))
self._socket.listen(1)
self._log("Waiting car connection...")
self._connection, addr = self._socket.accept()
self._log("Car socket connected")
self._connection = None
I've used more commonly-used names for the socket and connection, also adhering to the Python style guide.
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 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.
For convenience, I wanted to subclass socket to create an ICMP socket:
class ICMPSocket(socket.socket):
def __init__(self):
socket.socket.__init__(
self,
socket.AF_INET,
socket.SOCK_RAW,
socket.getprotobyname("icmp"))
def sendto(self, data, host):
socket.socket.sendto(self, data, (host, 1))
However, I can't override socket.sendto:
>>> s = icmp.ICMPSocket()
>>> s.sendto
<built-in method sendto of _socket.socket object at 0x100587f00>
This is because sendto is a "built-in method". According to the data model reference, this is "really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument."
My question: is there anyway to override built-in methods when subclassing?
[Edit] Second question: if not, why not?
I know this doesn't answer your question, but you could put the socket into an instance variable. This is what Nobody also suggested in the comments.
class ICMPSocket():
def __init__(self):
self.s = socket.socket(
socket.AF_INET,
socket.SOCK_RAW,
socket.getprotobyname("icmp"))
def sendto(self, data, host):
self.s.sendto(data, (host, 1))
def __getattr__(self, attr):
return getattr(self.s, attr)
Re-edit : My first solution wasn't working, and after straggling with this for sometime , i can conclude that in the case of python socket when you can say that aggregation is much better than inheriting but in case you want to know how you can do
it using inheritance check this code:
import socket
class ICMPSocket(socket.socket):
def __init__(self):
self._sock = socket.socket(
socket.AF_INET,
socket.SOCK_RAW,
socket.getprotobyname("icmp"))
# Delete the methods overrited by the socket initializer to make
# possible defining our own.
for attr in socket._delegate_methods:
try:
delattr(self, attr)
except AttributeError:
pass
def sendto(self, data, flags, addr):
return self._sock.sendto(data, flags, (addr, 1))
icmp = ICMPSocket()
print icmp.sendto('PING', 0, '127.0.0.1')
I try to program a TCPServer with threads (ThreadingMixIn) in Python. The problem is that I can't shut it down properly as I get the socket.error: [Errno 48] Address already in use when I try to run it again. This is a minimal example of the python code that triggers the problem:
import socket
import threading
import SocketServer
class FakeNetio230aHandler(SocketServer.BaseRequestHandler):
def send(self,message):
self.request.send(message+N_LINE_ENDING)
def handle(self):
self.request.send("Hello\n")
class FakeNetio230a(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
def __init__(self, server_address, RequestHandlerClass):
self.allow_reuse_address = True
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
if __name__ == '__main__':
for i in range(2):
fake_server = FakeNetio230a(("", 1234), FakeNetio230aHandler)
server_thread = threading.Thread(target=fake_server.serve_forever)
server_thread.setDaemon(True)
server_thread.start()
# might add some client connection here
fake_server.shutdown()
All the main code should do is to start the server, shut it down and run it again. But it triggers the error stated above because the socket has not been released after the first shutdown.
I thought that setting self.allow_reuse_address = True could solve the problem, but that did not work. When the python program finishes I can run it again straight away and it can start the server once (but again not twice).
However the problem is gone when I randomize the port (replace 1234 by 1234+i for example) as no other server is listening on that address.
There is a similar SO Q Shutting down gracefully from ThreadingTCPServer but the solution (set allow_reuse_address to True does not work for my code and I don't use ThreadingTCPServer).
How do I have to modify my code in order to be able to start the server twice in my code?
Some more information: The reason why I'm doing this is that I want to run some unit tests for my python project. This requires to provide a (fake) server that my software should to connect to.
edit:
I just found the most correct answer to my problem: I have to add fake_server.server_close() at the end of my main execution code (right after fake_server.shutdown()). I found it in the source file of the TCPServer implementation. All it does is self.socket.close().
Somehow, fake_server doesn't unbind when you assign to it (in first line in for statement).
To fix that, just remove fake_server at the end of loop:
del fake_server # force server to unbind
This post helped me get over the un-closed socket problem.
I had the same problem and wanted to post here my simple implementation for TCP server class (and client method).
I made a TCPThreadedServer class. In order to use it is needed to be inherited, and the method process(msg) must be overridden. the overridden method invokes every time the server gets a message msg, and if it returns a not None object, it will be returned as string to the connected client.
from SocketServer import TCPServer, StreamRequestHandler, ThreadingMixIn
import threading
class TCPThreadedServer(TCPServer, ThreadingMixIn):
class RequstHandler(StreamRequestHandler):
def handle(self):
msg = self.rfile.readline().strip()
reply = self.server.process(msg)
if reply is not None:
self.wfile.write(str(reply) + '\n')
def __init__(self, host, port, name=None):
self.allow_reuse_address = True
TCPServer.__init__(self, (host, port), self.RequstHandler)
if name is None: name = "%s:%s" % (host, port)
self.name = name
self.poll_interval = 0.5
def process(self, msg):
"""
should be overridden
process a message
msg - string containing a received message
return - if returns a not None object, it will be sent back
to the client.
"""
raise NotImplemented
def serve_forever(self, poll_interval=0.5):
self.poll_interval = poll_interval
self.trd = threading.Thread(target=TCPServer.serve_forever,
args = [self, self.poll_interval],
name = "PyServer-" + self.name)
self.trd.start()
def shutdown(self):
TCPServer.shutdown(self)
TCPServer.server_close(self)
self.trd.join()
del self.trd
I found it quite easy to use:
class EchoServerExample(TCPThreadedServer):
def __init__(self):
TCPThreadedServer.__init__(self, "localhost", 1234, "Server")
def process(self, data):
print "EchoServer Got: " + data
return str.upper(data)
for i in range(10):
echo = EchoServerExample()
echo.serve_forever()
response = client("localhost", 1234, "hi-%i" % i)
print "Client received: " + response
echo.shutdown()
I used the method:
import socket
def client(ip, port, msg, recv_len=4096,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
msg = str(msg)
response = None
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((ip, port))
if timeout != socket._GLOBAL_DEFAULT_TIMEOUT:
sock.settimeout(timeout)
sock.send(msg + "\n")
if recv_len > 0:
response = sock.recv(recv_len)
finally:
sock.close()
return response
Enjoy it!
Change your FakeNetio230a definition to this:
class FakeNetio230a(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
def __init__(self, server_address, RequestHandlerClass):
self.allow_reuse_address = True
SocketServer.TCPServer.__init__(self,
server_address,
RequestHandlerClass,
False) # do not implicitly bind
Then, add these two lines in your entry point below your FakeNetio230a instantiation:
fake_server.server_bind() # explicitly bind
fake_server.server_activate() # activate the server
Here's an example:
if __name__ == '__main__':
for i in range(2):
fake_server = FakeNetio230a(("", 1234), FakeNetio230aHandler)
fake_server.server_bind() # explicitly bind
fake_server.server_activate() # activate the server
server_thread = threading.Thread(target=fake_server.serve_forever)
server_thread.setDaemon(True)
server_thread.start()
# might add some client connection here
fake_server.shutdown()