Below you can see a simple TCP echo server I've just coded. It uses .recv call to read a client's data.
Everything works fine, I can see messages I sent but... they could be any size. 1 byte, 10 bytes, 60 bytes, 1024 bytes. I thought that .recv call blocks the application until the buffer is filled with the data. But it doesn't!
I don't get how .recv call understands that there is nothing to read from a socket anymore and "we may return this data to the caller, anyway there is nothing to read anymore and we don't wait for the next bytes to fill the buffer".
I even read man pages of recv and read system calls.
I'd appreciate any help or useful links to read about it.
I'd like to clarify, the questions are:
- How the .recv call understands that there is no more bytes to read from socket?
- How to force .recv call to wait until the buffer is full?
- If .recv call waits for some time (like timeout) until it returns the data, how would I be able to change this timeout to another value?
import socket
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 43542))
s.listen()
while True:
try:
client, addr = s.accept()
except Exception:
s.close()
break
client.settimeout(12345)
result = client.recv(1024)
print('message:', result.decode('utf-8'))
s.close()
UPD.
Client code:
import socket
import time
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 43542))
message = "hello!"
for i in range(6):
s.send(message[i].encode('utf-8'))
time.sleep(1)
s.close()
tcp dump output:
sudo tcpdump tcp -i lo0 -vv -K
tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
13:34:19.880009 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
localhost.61201 > localhost.43542: Flags [S], seq 24512730, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 1467040500 ecr 0,sackOK,eol], length 0
13:34:19.880063 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
localhost.43542 > localhost.61201: Flags [S.], seq 2392598781, ack 24512731, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 16287257 ecr 1467040500,sackOK,eol], length 0
13:34:19.880069 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52)
localhost.61201 > localhost.43542: Flags [.], seq 1, ack 1, win 6379, options [nop,nop,TS val 1467040500 ecr 16287257], length 0
13:34:19.880075 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52)
localhost.43542 > localhost.61201: Flags [.], seq 1, ack 1, win 6379, options [nop,nop,TS val 16287257 ecr 1467040500], length 0
13:34:19.880085 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 53)
localhost.61201 > localhost.43542: Flags [P.], seq 1:2, ack 1, win 6379, options [nop,nop,TS val 1467040500 ecr 16287257], length 1
13:34:19.880093 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52)
localhost.43542 > localhost.61201: Flags [.], seq 1, ack 2, win 6379, options [nop,nop,TS val 16287257 ecr 1467040500], length 0
13:34:19.882033 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52)
localhost.43542 > localhost.61201: Flags [F.], seq 1, ack 2, win 6379, options [nop,nop,TS val 16287259 ecr 1467040500], length 0
13:34:19.882049 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52)
localhost.61201 > localhost.43542: Flags [.], seq 2, ack 2, win 6379, options [nop,nop,TS val 1467040502 ecr 16287259], length 0
13:34:20.885298 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 53)
localhost.61201 > localhost.43542: Flags [P.], seq 2:3, ack 2, win 6379, options [nop,nop,TS val 1467041505 ecr 16287259], length 1
13:34:20.885451 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
localhost.43542 > localhost.61201: Flags [R], seq 2392598783, win 0, length 0
I see that client sends a TCP segment with PUSH flag. I found an answer for that question https://superuser.com/questions/1455476/what-does-tcp-packet-p-flag-means-in-tcpdumps-output which told me that using PUSH flag forces receiver to give that information to an application as fast as possible.
So, now I understand why server gives the data to an application. But I still can't get why server sends a FIN segment to a client. Why does he want to close a connection?
My expectation were:
- Client sends data byte-by-byte sleeping for 1 second in iteration
- Server wait for the data
- Server prints data
UPD 2.
I'm dumb, I have to be shamed ha-ha.
- Server's code is waiting for the data.
- Client sends data (1 byte). With TCP PUSH flag.
- Server receive the segment and found PUSH flag (which forces the net-stack to give a control to an application).
- Application sees that 1 byte.
- Connection is closed by the application.
So, I have to write my-own buffer on an application layer.
Thank you guys for your help.