How sending and receiving works in Python sockets? - python

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

Related

Asynchronous receiving TCP packets in 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

Basic TCP Connection

I am trying to control some test equipment with a TCP connection. The equipment comes with software that you are able to control over TCP. Basically, you can input the IP address and port of the client computer and there is also an indicator light that shows when there is an open listening session on that port (this is all on the equipment software interface)
I have tested this using SocketTest3 (free software) and am able to start a listening session as well as send commands from another computer. Now, I want to control the equipment with Python. I am running the code for the server and client on the same machine as the test equipment (using local IP address). When I simply run the code (with the equipment software closed) I am able to send, receive, and print the messages I send. When I have the equipment software open (necessary for control) I am able to start a listening session (indicator light shows up on equipment software), but nothing happens (no errors and nothing received) when I send commands. The messages are also not sent back to the client to print.
Any ideas? It's probably something very simple that I'm missing.
Server code:
import sys
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
TCP_IP = '127.0.0.1'
TCP_PORT = 8001
s.bind((TCP_IP, TCP_PORT))
s.listen(5)
connection, client_address = s.accept()
BUFFER_SIZE = 20
print 'Address: ', client_address
while 1:
print "receiving..."
data = connection.recv(BUFFER_SIZE)
print data
if not data: break
print "received data:", data
connection.send(data) # echo
connection.close()
Client code:
import socket
TCP_IP = '127.0.0.1'
TCP_PORT = 8001
BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
print "sending message..."
s.sendall('ST<CR>') # Send command
print "receiving message..."
data = s.recv(BUFFER_SIZE)
s.close()
print "received data:", data
For those wondering what is missing from camerausb's code, I think he was not seeing anything from the client's print statement because he did not use repr() to format the data. I had a similar problem, but this worked for me:
print 'Received', repr(data)

Python - Simple socket use

I want to create a simple communication between a server and a client using sockets. The cliend is supposed to send a message and then the server sends a message to the client.
This is my Client code :
import socket
s = socket.socket()
HOST = '127.0.0.1'
s.connect((HOST, 1234))
s.send('Hi')
print ('Client send')
print s.recv(1024)
s.close
This is my Server's code :
import socket
s = socket.socket()
HOST = '127.0.0.1'
s.bind((HOST, 1234))
s.listen(5)
while True:
c, addr = s.accept()
c.send('Hi client')
c.close()
But it only prints "Client send " .
In your server, after having sent 'Hi client' you must wait for the client to have read the message.
You could do either of two things:
Use shutdown() on the socket in the server, see https://docs.python.org/2/library/socket.html#socket.socket.shutdown
Do a .recv(..) in the server, which will terminate after the client has close'ed
the socket after reading the reply the server sent.
Update: tried it on my system (MacOSX). Started two python interpreters. Pasted the server code verbatim in one; server is now up and running and accepting connections.
In the other python interpreter, the client shell, I did the following
>>> import socket
>>> HOST = '127.0.0.1'
>>> def test():
... s = socket.socket()
... s.connect((HOST, 1234))
... s.send('Hi')
... print s.recv(1024)
... s.close() # <== Note function call here!
...
>>> test()
Hi client
>>> test()
Hi client
>>> test()
Hi client
>>> test()
Hi client
This demonstrates that - at least on my system - the code works as anticipated.
I can't reprocude your problem and therefore vote to close your question.
Here's the code I used:
import socket
import threading
HOST = '127.0.0.1'
PORT = 12345
def client():
s = socket.socket()
s.connect((HOST, PORT))
s.send('Hi')
print ('Client send')
print s.recv(1024)
s.close()
def server():
s = socket.socket()
s.bind((HOST, PORT))
s.listen(5)
c, addr = s.accept()
c.send('Hi client')
c.close()
if __name__ == "__main__":
server = threading.Thread(target=server)
server.start()
client()
server.join()
Here's the output I got:
$ python test.py
Client send
Hi client
If the problem persists, please add additional details to your question about why and how your setup still not works. Maybe the problem is just about how you run the programs. Please add details about this as well.
The underlying problem is that you treat sockets as if they were message-busses (one message at a time, always received fully), where in fact they are streams of bytes.
Nobody guarantees you that sending X bytes of data will reach the opposite side as that. It could be X1, X2, X3, Y1 where X1+X2+X3=X, and Y1 being the beginning of the next message Y. And all other kinds of combinations and errors.
To remedy this, real-world client-server apps use protocols. Take HTPP for example: a request starts always with HTTP, and it ends with two newlines. Within this block, there is the Content-Length header telling the client how many bytes to read then - regardless of chunk sizes.
So to really solve your problem, you either have to write a full fledged protocol, or built upon libraries that do that for you - e.g. Twisted, Nanomsg or ZeroMQ

UDP Connect Cause Socket To Lose Packets?

I was debugging a python program, the application can't receive udp packets as expected. Finally I found it was the UdpSocket.connect cause the UdpSocket to lose these packets. See below code:
def main:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect((server_ip, 9)) #This results in the issue
#Use sock.bind((local_ip, 12345))
#instead would solve the problem
localip, localport = sock.getsockname()
packet = GetRegisterData(localip, localport)
sock.sendto(packet, (server_ip, 5000)) #the host (server_ip, 5000) will
#always send back two udp packets
#to respond this statement
sleep(1)
while True:
response = sock.recv(1024) #packets from (server_ip, 5000) reached
#machine but this statement never return
if not len(response)
break
print response
I am very new to Python, and don't understand why this would happen. Any body helps explain this?
[Update]
I use tcpdump to capture packets, only to find the lost packets have reached the machine, but due to unkown reason the sock.recv just doesn't retuern. I want somebody to help explain why the sock.recv doesn't return everytime here.
You didn't mention where the packets that you expect to receive (but fail to) are coming from. I'm guessing they're not coming from the address you connected to, though. See the man page for connect(2) - which is what you're calling when you use this Python API - for information about why this matters. In particular:
If the socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received.
(emphasis mine).

Passing _socketobjects as parameters

I'm writing a multithreaded distributed networking algorithm.
I've one thread that listens to new connections. Every time a new connection is established a separate thread is started for listening to messages from that connection.
My problem is that the socket I open works perfectly in both directions inside the connection listener. After I pass the socket object for that connection to the message listener I can read data from the socket, but sending data through it doesn't reach the remote host.
Here's the essential snip from my code:
def connection_listener(port, start_e, terminate_e):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.settimeout(1)
s.bind(('', port))
s.listen(1)
while (not start_e.isSet()):
try:
conn, addr = s.accept()
msg_in = conn.recv(1024).split(":")
if (msg_in[1]=="hello"):
# If addr sends us a 'id:hello', we reply with a 'my_id:welcome'
conn.send(str(my_id)+":welcome")
t = Thread(target=message_listener, args=(conn, addr[0], terminate_e, ))
t.start()
except:
pass # timeout
def message_listener(conn, address, terminate_e):
while (not terminate_e.isSet()):
try:
msg_in = conn.recv(1024)
# Here I can receive everything that I send from the other end of conn,
# but conn.send("any data") doesn't reach the remote host
What I'd like to do is send acknowledgement-like messages from the message listener thread using the conn. Is this possible somehow or am I thinking and doing it wrong?
I sorted this out myself, so I'll share my answer.
I made the protocol exchange fixed size messages by padding with zeroes up to the desired length. I used a length of 32 bytes, which might be quite tiny from the hardware's point of view. Nevertheless it seems to work as supposed.
Pragmatically my solution looks like:
def send_everyone(message):
for i in range(len(peers)):
chunk = (str(my_id)+":"+message).rjust(32, '0')
peers[i].send(chunk)
And on the receiving side we want only 32 bytes at a time:
def message_listener(conn, address, terminate_e):
while (not terminate_e.isSet()):
try:
msg_in = conn.recv(32)
...

Categories

Resources