I created socket for two PC, one is Raspberry Pi and the other one is my laptop. I just connected two then I send string to test the connection. If I send a character "q" from the RPi, my PC should break out of the loop and close the connection but it does not. The part print("Listening") is still running. Why? See code below.
import socket
import time
# IP address of this PC.
TCP_IP = '192.168.137.1'
# Port.
TCP_PORT = 5005
# Size of buffer.
BUFFER_SIZE = 1024
# Create a socket, connect and listen to it.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print('Connection address:', addr)
while 1:
print("Listening")
data = conn.recv(BUFFER_SIZE)
data = data.decode()
if data=='q':
break
if data:
print ("Received data:", data)
# Echo back.
conn.send(data.encode())
time.sleep(1)
print("It breaks.")
conn.close()
s.close()
TCP is a stream oriented protocol. So data transmitted is a stream not a sequence of messages. So when you expect data to be q it actually is some_data_sent_before_q_and_finally_q.
The simplest way to repair the code is to use if data.endswith('q') instead of if data=='q'. May work and may not depending on how you actually use the connection. For example, this approach may fail with some_data_sent_before_q pretty long pause more_data_and_q and with some_data_sent_before_q_and_finally_q_plus_something_else_why_not.
Little bit more advanced way to solve the problem is to divide the stream into messages with separators - message_1<separator>message_2<separator>q<separator>. This method will allow you to treat every message separately.
Related
I copied the echo server example from the python documentation and it's working fine. But when I edit the code, so it wont send the data back to the client, the socket.recv() method doesn't return when it's called the second time.
import socket
HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data: break
conn.sendall(b'ok')
conn.close()
In the original version from the python documentation the while loop is slightly different:
while True:
data = conn.recv(1024)
if not data: break
conn.sendall(data)
Client's code:
import socket
HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
s.close()
print('Received', repr(data))
TCP sockets are streams of data. There is no one-to-one correlation between send calls on one side and receive calls on the other. There is a higher level correlation based on the protocol you implement. In the original code, the rule was that the server would send exactly what it received until the client closed the incoming side of the connection. Then the server closed the socket.
With your change, the rules changed. Now the server keeps receiving and discarding data until the client closes the incoming side of the connection. Then the server sends "ok" and closes the socket.
A client using the first rule hangs because its expecting data before it closes the socket. If it wants to work with this new server rule, it has to close its outgoing side of the socket to tell the server its done, and then it can get the return data.
I've updated the client and server to shutdown parts of the connection and also have the client do multiple recv's in case the incoming data is fragmented. Less complete implementations seem to work for small payloads because you are unlikely to get fragmentation, but break horribly in real production code.
server
import socket
HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data: break
conn.sendall(b'ok')
conn.shutdown(socket.SHUT_WR)
conn.close()
client
import socket
HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
s.shutdown(socket.SHUT_WR)
data = b''
while True:
buf = s.recv(1024)
if not buf:
break
data += buf
s.close()
print('Received', repr(data))
The number of receive and send operations have to match because they are blocking. This is the flow diagram for your code:
Server listen
Client connect
Server receive (this waits until a message arrives at the server) [1]
Client send 'Hello world' (received by [1])
Server receive (because there was data received) [2]
Client receive [3]
Because the server and the client are blocked now, no program can continue any further.
The fix would be to remove the client's receive call because you removed the server's send call.
I have a python program that is supposed to receive some arbitrary bytes and send them back after receiving a fin. I already was able to implement this as you can see below.
The problem I am having is that the connection is never properly closed. Using ss -tan I can see that the connection keeps being stuck in LAST_ACK state. This is although the connection seems to be closed correctly looking at the Wireshark packet trace. I have attached an Image of the Wireshark packet trace that is the result of first sending "AAAAA" then an out of order "BBB" and then filling the hole with "CCC".
Looking at the Wireshark packets I think that all packets should be correctly acknowledged and the connection shoud terminate normally without being stuck in LAST_ACK state and without doing the retransmissions at the bottom. But I Still would guess that there is a problem with the connection closing packets.
# Echo server program
import socket
HOST = '' # Symbolic name meaning all available interfaces
PORT = 6000 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print ('Connected by', addr)
data_acc = b''
while 1:
data = conn.recv(1024)
if not data: break
data_acc += data
print(data)
print("send data back")
conn.sendall(data_acc)
conn.close()
I found the answer to the problem. The sequence number of ACK on line 56 in the screenshot needs to be increased by one. By doing so Wireshark also stops interpreting it as a keep alive packet.
I am trying to learn Python sockets and have hit a snare with the socket.accept() method. As I understand the method, once I call accept, the thread will sit and wait for an incoming connection (blocking all following code). However, in the code below, which I got from https://docs.python.org/2/library/socket.html and am using localhost. I added a print('hello') to the first line of the server. Yet the print doesn't appear until after I disconnect the client. Why is this? Why does accept seem to run before my print yet after I bind the socket?
# Echo server program
import socket
print('hello') # This doesn't print until I disconnect the client
HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
if not data: break
conn.sendall(data)
conn.close()
# Echo client program
import socket
HOST = 'localhost' # The remote host
PORT = 50007 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
You are likely using an output device on a system that Python's IO doesn't recognize as interactive. As a workaround, you can add sys.stdout.flush() after the print.
The standard output is a buffered stream, meaning that when you print something, the output sticks around in an internal buffer until you print enough data to fill the whole buffer (unlikely in a small program, the buffet is several kilobytes in size), or until the program exits, when all such buffers are automatically flushed. Normally when the output is a terminal service, the IO layer automatically switches to line buffering, where the buffer is also flushed whenever a newline character is printed (and which the print statement inserts automatically).
For some reason, that doesn't work on your system, and you have to flush explicitly. Another option is to run python -u, which should force unbuffered standard streams.
I'm writing a simple client server app in python, where the client is listening every type of data entering in the specific port, and I want to when receiving a data flow, send back to the connected client (which have a dinamic ip) a string, in this case "001". But when I try to send the message, it fails!
#!/usr/bin/env python
import socket
TCP_IP = '192.168.1.115'
TCP_PORT = 55001
BUFFER_SIZE = 1024
MESSAGE = '01'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print ('Connection address:', addr)
while 1:
data = conn.recv(BUFFER_SIZE)
if not data: break
print ('received data:', data)
conn.send(data) # echo
print ('Sending data to client...')
addr change every connection .. i cannot manage this!
s.connect((addr, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()
(Connected stream) sockets are bidirectional, so there's no need to call connect to get a connection to the client—you already have one.
But you want to know why your code fails. And there are at least three problems with it.
First, after you call listen or connect on a socket, you can't call connect again; you will get an exception (EISCONN on POSIX, something equivalent on Windows). You will have to create a new socket.
Second, is client actually binded and listening for a connection on the same port as the server? If not, your connect can't work. If so, the bind will fail if the client and server are on the same machine.
Third, the addr you get back from accept is a (host, port) pair, not just a host. So, as written, you're trying to connect((('192.168.1.115', 12345), 55001)), which obviously isn't going to work.
You are trying to reply to the client using the server listening socket (s). This is only possible in UDP Servers. Since this is a TCP Server you have to use the conn which is crated using s.accept() to communication with remote client.
I'm having a little trouble with sockets in Python. Whenever someone connects it works fine but if they disconnect the server program closes. I want the server program to remain open after the client closes. I'm using a while True loop to keep the connection alive but once the client closes the connection the server closes it's connection.
Here is the client:
import socket, sys
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = sys.argv[1]
port = int(sys.argv[2])
conn.connect((host, port))
print("Connected to host " + sys.argv[1])
td = 1
while td == 1:
msg = raw_input('MSG: ')
Here is the server:
import socket, sys
socket.setdefaulttimeout(150)
host = ''
port = 50005
socksize = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
print("Server started on port: %s" % port)
s.listen(1)
print("Now listening...\n")
conn, addr = s.accept()
while True:
print 'New connection from %s:%d' % (addr[0], addr[1])
data = conn.recv(socksize)
if not data:
break
elif data == 'killsrv':
conn.close()
sys.exit()
else:
print(data)
If a client closes a connection, you want it to close the socket.
It seems like there's a bit of a disconnect here that I'll try to elaborate on. When you create a socket, bind, and listen, you've established an open door for others to come and make connections to you.
Once a client connects to you, and you use the accept() call to accept the connection and get a new socket (conn), which is returned for you to interact with the client. Your original listening socket is still there and active, and you can still use it to accept more new connections.
Looking at your code, you probably want to do something like this:
while True:
print("Now listening...\n")
conn, addr = s.accept()
print 'New connection from %s:%d' % (addr[0], addr[1])
data = conn.recv(socksize)
if not data:
break
elif data == 'killsrv':
conn.close()
sys.exit()
else:
print(data)
Please note that this is just a starting point, and as others have suggested you probably want to use select() along with forking off processes or spawning threads to service each client.
Your code is only accepting a single connection - the loop only deals with the first accepted connection and terminates as soon as it lost. This is way your server exists:
data = conn.recv(socksize)
if not data:
break
What you will need to do is to accept several connections, while handling each of those in it's own loop. Note that it does not have to be a real loop for each socket, you can use a select-based approach to query which of the sockets has an event associated with it (data available, connection lost etc.) and then process only those sockets, all in the same loop.
You can also use a multi threaded / multi process approach, dealing with each client in it's own thread or process - I guess you won't run into scaling issues when playing around.
See:
http://docs.python.org/library/select.html
http://docs.python.org/library/multiprocessing.html