Asynchronous receiving TCP packets in python - python

I have an instant messaging app, with client written in python.
Code that connects to a server:
def connect(self, host, port, name):
host = str(host)
port = int(port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send('CONNECT:' + name)
print s.recv(1024)
return s
Then s will be stored in a self.socket.
Here is function that receives tcp packets from server and prints it to command line.
def chat(self):
while True:
data = self.socket.recv(4096)
if not data:
pass
else:
print data
So in my mind it should receive everything server sends and prints out, but it isn't happens. Anyone knows how to bring it to life?

There is a way with select function to monitor multiple streams, make a list of all the streams you need to handle and use the select function for it, for the user input use sys.stdin and all the sockets that you expect to chat with.
check this: https://docs.python.org/2/library/select.htmlBut still the best way to do asynchronous chat will be with udp, it will work really fine

Related

How to use a Python 3 socket client/server setup over the internet (not locally)?

I am trying to implement a simple chat program that uses sockets to transmit data via a UDP connection. However, I can't figure out how to correctly set it up so that people from outside my local network can access it if I am hosting it on my laptop. I am utilizing port 5000, and have port-forwarded that port on my router for my laptop. The port-forwarding doesn't seem to be the issue; at least the "Port Forward Network Utilities" from portforward.com seems to detect it as properly forwarded. Maybe I am mixing up the IP addresses I need to host from and connect with? The code in question is below:
import socket
import threading
import sys
class Server:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connections = []
def __init__(self):
self.sock.bind(('192.168.1.5', 5000))
self.sock.listen(1)
def handler(self, c, a):
while True:
data = c.recv(1024)
for connection in self.connections:
print(data.decode())
connection.send(data)
if not data:
break
def run(self):
while True:
c, a = self.sock.accept()
cThread = threading.Thread(target=self.handler, args=(c, a))
cThread.daemon = True
cThread.start()
self.connections.append(c)
print(self.connections)
class Client:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
usr_name = ""
def sendMsg(self):
while True:
self.sock.send(bytes(self.usr_name + ": " + input("-> "), 'utf-8'))
def __init__(self, address):
self.sock.connect((address, 5000))
self.usr_name = input("Enter a username: ")
iThread = threading.Thread(target=self.sendMsg)
iThread.daemon = True
iThread.start()
while True:
data = self.sock.recv(1024)
if not data:
break
print(data.decode())
if len(sys.argv) > 1:
client = Client(sys.argv[1])
else:
server = Server()
server.run()
As you can see, I have my current local IP address inputted for hosting the server, while the client asks for an IP to connect to. I'm not sure what to do now for hosting this over the internet, but I have tried every IP combination I can think of and it returns a number of errors.
Thanks in advance.
Edit:
The two main errors I was getting are:
Timeout Error [WinError 10060]
My friend received this when trying to connect from another network
[WinError 10061]
I would receive this when trying to connect using my public IP from the same computer
I'm sorry that I can't be more detailed in my errors and provide a full printout, and I will try to update this if I'm able to replicate them.
Edit:
I was able to rewrite it and get it to work, I don't need anymore help with this.
Thanks.
You're port-forwarding UDP port 5000 to 5000.
But you're opening TCP streams, not UDP. That's what SOCK_STREAM means. If you want UDP, you need to use SOCK_DGRAM.
So, you need to make these two consistent. The only problem is, I'm not sure which one you actually want here.
On the one hand, your code is doing connection-oriented recv, and seems to be assuming reliable transmission, which means you probably want TCP.
On the other hand, your code seems to be assuming that each recv(1024) is going to get exactly one send from the other side, which is only true for UDP; TCP sockets are byte streams, not message streams. When you do a recv(1024), you could easily get just the first 12 bytes of an 80-byte line, which means it could end in the middle of a UTF-8 character, which means decode will throw an exception.
I think you want TCP, but with a framing protocol on top of it. The simplest protocol that would probably make sense here is lines of text. Which is pretty easy to do on your own, but even easier to do with socket.makefile, given that you're dedicating a thread to each connection.

react with _socketobject address in memory

I'm trying to access socket objects from memory address "socket._socketobject object at 0x7f4c39d78b40" and use it for another function at different times. The clients are connected to port 9999 and I want the server to react with each one at a later stage while keeping the connection up.
def sock_con(host,port):
host = host
port = port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(5)
while True:
client, address = sock.accept()
print client
print type(client)
print "Server (%s, %s) connected" % address
mongoconn = connectionx('IP_Clients')
key = {'addresses':'192.168.11.1'}
data = {'client':str(client), 'addresses':address}
mongoconn.update(key, data)
client.settimeout(60)
The next code is at a different module which can be used at anytime:
import os,sys
import socket
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)
from mgodb import connectionx
mongoconn = connectionx('IP_Clients')
x= mongoconn.find_one({'addresses':'192.168.11.1'})
client= eval(x['client'])
def send_stuff(client,addresses,arg1):
while True:
try:
#data = client.recv(size)
print data
client.send(arg1)
return data
except:
#raise error('Client disconnected')
client.close()
return False
send_stuff(client,x['addresses'],'test10')
To use sockets later in the same process, just store them at their arrival and find them later. Something like this:
...
clients = {}
while True:
client, addr = server.accept()
clients[addr[0]] = client
So, if you stop the listening loop, or run it in a thread, or you run something else in a thread (doesn't matter), you can get the opened socket object from dictionary clients by the client's IP address.
client = clients.get("192.168.1.1")
But you should count in the port as well for detection, because there may be two different clients contacting you from same IP address.
If you want to send an opened socket to another process, well, it is doable but not worth the trouble.
You would need to send the socket's filedescriptor ( socket.fileno() ) to another process, and that can be done using Python module sendfds. It can be found on pypi.python.org.
Then, in receiving process, you would have to construct the socket wrapper object around it manually or trick somehow the existing _socket.dll/.so and socket.py modules to do it for you.
A lot of work and success dubious. What you should do instead is to use the dictionary to store sockets and create an interface (over socket, PIPE or whatever IPC) to forward messages to and from needed connected sockets.
Finally, you do not have to worry about this mess at all, because Python has asyncore module.
It already does the socket storing into dictionary and other useful stuff. The thing is, you need to know what you want to achieve to be able to adequately tune the asyncore client handler. Set correct buffer sizes etc. etc. But asyncore is elegant and you can easily mix it with existing GUI event loop. asyncore and asynchat are often used when creating push servers or instant-messaging-like systems.

Python broadcasting message to all clients in a socket

I have made a simple chat server using threads like the following:
#-*- coding:utf-8 -*-
import _thread as thread
import time
import socket
def now():
return time.asctime(time.localtime())
def handleclient(connection, ADDR):
sod = str(ADDR)
msg = sod+"joined the chat"
msg2 = msg.encode("utf-8")
connection.sendall(msg2)
while True:
recieved = connection.recv(1024)
adsf = recieved.decode("utf-8")
print(now(),"(%s):%s" % (ADDR, recieved))
output = "%s:%s"%(ADDR, recieved.decode("utf-8"))
message = output.encode("utf-8")
connection.sendall(message)
if __name__ == "__main__":
addr = ("", 8080)
r =socket.socket()
print("socket object created at", now())
r.bind(addr)
r.listen(5)
while True:
print("Waiting for clients...")
connection, ADDR = r.accept()
print("We have connection from ", ADDR)
thread.start_new_thread(handleclient, (connection, ADDR))
However, it looks like the sendall isnt working and sending the message to only the person who sent it. How can I make it send it to all clients?
There is nothing like what you're trying to do, because as pointed out in the commends, sendall() means "definitely send all my bytes and keep trying until you have," not "send these bytes to lots of clients."
You will want to use either UDP multicast (if you're on a relatively reliable network which supports it, such as a LAN or corporate WAN), or you will simply need to send explicitly to every connected client. The other alternative is peer-to-peer: send to several clients and instruct those clients to send to more clients until all clients are taken care of. Obviously this requires more coding. :)
You may have a look at Zero MQ, which provides high-level facilities over sockets by implementing several patterns ( publish/subscribe , push/pull, etc...).

How sending and receiving works in Python sockets?

I'm working with python sockets for a while, and I wrote some simple programs.
The problem that I encountered is related to sending/receiving methods in python sockets.
Giving you a basic example:
This is the receiver (server):
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 4001))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.listen(5)
while True:
conn, addr = s.accept()
print conn, addr
data1 = conn.recv(64)
data2 = conn.recv(64)
print 'uname is %s , password is: %s' %(data1, data2, )
conn.close()
And this is the sender (or client):
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('', 4001))
uname = raw_input('enter username>')
passw = raw_input('enter password>')
s.send(uname)
s.send(passw)
print 'exiting ...'
s.close()
So the problem is: why server receives both uname and passw in first s.recv() method? It means data2 is always empty!
I have no idea what happens when client executes the s.send() method. I was thinking that each s.send() actually sends a "packet" to the destination (ip, port)!
Can someone explain to me why the second code is working correctly?
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('', 4001))
uname = raw_input('enter username>')
s.send(uname)
passw = raw_input('enter password>')
s.send(passw)
print 'exiting ...'
s.close()
socket.SOCK_STREAM means you're communicating via TCP . This means, if you call send, your data is pushed to the system's networking stack. Both send calls are called shortly one after another.
If you use TCP, your system decides about the packet size. So the uname and passw might be sent in one packet or even split in any other way.
On the receiver's side, data1 receives up to 64 bytes, which is enough for uname and passw.
The explanation above also shows, why the second one works:
Here you need some time between sending uname and passw. During this time, your OS decides to send the packet (i.e. to flush the network buffer).
When you are using streams, you should not think in terms of packets but in terms of streams. There a send call only means: push some data on my network stack(like a pipeline).
If you are interested in packets,
you might try to experiment with UDP:
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
With such kind of socket your first sender would work as expected
I also faced similar problem. Then I implemented a protocol to send and receive one message at a time. Hope this link will help a lot : LINK

Test a pair of network sockets at the same time

I have an app X that can run on either of two computers, but on no more than one at once. I have another app Y, written in Python, that given the two possible ip addresses needs to find out which computer is running app X (if any). I've partially solved this by having a UDP service that listens on a port and responds with a 'Hello' whenever it receives some data. The client can try and send data to the app X port on each address and if it gets a response, I know the application is running on that computer.
My code so far looks like this:
def ipaddress(self):
"""Test which side responds on the status port."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.settimeout(5)
s.sendto("status", (ADDR_A, PORT))
s.recvfrom(1024)
except socket.timeout:
try:
s.sendto("status", (ADDR_B, PORT))
s.recvfrom(1024)
except:
pass
else:
return ADDR_B
else:
return ADDR_A
finally:
s.close()
return None
The problem with this function is that it's called periodically whenever I want to talk to the computer running app X. It will always test ADDR_A first, and if it's not running app X then I have to wait for the socket to timeout before trying ADDR_B. Although it doesn't happen often app X could have switched computers whenever I come around trying again.
Is there a better way? I'm wondering if it's possible to connect to both computers in parallel and return as soon as one responds? Or should I cache which ip address responded first last time the function was called? How would I code these or other ideas?
Thanks.
EDIT: Here is my revised code using select:
def ipaddress(addr_a, addr_b, timeout=5):
"""Test which side responds on the status port."""
# Create UDP sockets for each address
socks = [ socket.socket(socket.AF_INET, socket.SOCK_DGRAM),
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
]
# Send some data to each socket
for sock, addr in zip(socks, (addr_a, addr_b)):
sock.connect(addr) # do explicit connect so getpeername works
sock.send("status")
# Wait for the first to respond if any
while socks:
waiting = select.select(socks, [], socks, timeout)[0]
if waiting:
for sock in waiting:
try:
data = sock.recv(1024)
if data:
return sock.getpeername()[0]
except Exception, e:
# Occasionally get [Errno 10054] which means socket isn't really
# available, so see if other responds instead...
socks.remove(sock)
else:
break # timeout occurred
return None
You should look at select.select() which provides exactly the capability you are looking for to look at the two computers in parallel.

Categories

Resources