m2568475
m2568475

Reputation: 1

Packetize TCP stream

In my program, I have the following schema:

Client - intermediate router - server

The client and router are connected with TCP connection. Router and server has UDP connection. I am sending the data as this:

messagetobesend = "some very long message to be send"
sock = socket(AF_INET,SOCK_STREAM)
# bind my port to the socket
sock.bind((IP,PORT))
sock.connect((routerip, routerport))

# since I'm using TCP, send the data byte by byte
for i in range(len(messagetobesend)):
     sock.send(messagetobesend[i])

# now, I have send all of the message
sock.close()

In the router, I have the following:

sock = socket(AF_INET,SOCK_STREAM)
sock.bind((ip, port))
sock.listen(1)
# I have accepted the TCP connection
newsocket, addr = sock.accept()

# now create a UDP socket
udpsock = socket(AF_INET,SOCK_DGRAM)
udpsock.bind((myudpip, myudpport))

while True:
    # I want to packetize the data with 128 bytes
    data = newsocket.recv(128)
    if(data):
        data.sendto(udpsock,(serverip,serverport))
    else:
        #received no data, terminate udp connection
        udpsock.close()

newsocket.close()

And the server:

sock = socket(AF_INET,SOCK_DGRAM)
sock.bind((myip, myport))
while True:

    data, addr = sock.recvfrom(128)
    #print data

Everything I do seems correct. However, the server usually receives packets as 128 bytes, sometimes receives most of them 128bytes and some of them not 128 bytes.

Am I doing something wrong here? I want server to receive all of these packets with length of 128 bytes(all received packets length should be identical)

Upvotes: 0

Views: 555

Answers (1)

Andrew Morozko
Andrew Morozko

Reputation: 2816

Your recv call returns up to 128 bytes. If you need to receive exactly 128 bytes, then you should write your own recv_exactly:

PACKET_LEN = 128

# ...

def recv_exactly(socket, length):
    buf = bytearray()
    bytes_left = length

    while bytes_left: # equivalent to while bytes_left != 0:  
        data = socket.recv(bytes_left)
        if not data:
            return buf # could theoretically be less than length
        buf += data
        bytes_left -= len(data)

    return buf

while True:
    data = recv_exactly(newsocket, PACKET_LEN)
    if (len(data) == PACKET_LEN): # Data fully received 
        udpsock.sendto(data, (serverip, serverport))
    elif (len(data) > 0): # Data partially received 
        udpsock.sendto(data, (serverip, serverport))
        udpsock.close()
    else: # got no data
        udpsock.close()

P.S.: You can send whole messagetobesend, sending it byte by byte is not necessary. OS kernel and network interface would buffer and split your bytes however they need to.

And generally: TCP is a stream of bytes that will end when the socket would be closed. It arrives in unpredictable chunks, independent of how much data was sent in each send call. TCP guarantees the order and integrity of the stream, but does not have packet boundaries.

UDP is unreliable, can arrive with mangled data, in a wrong packet order, duplicated, or not arrive at all. But UDP is packet oriented, meaning that receiver can determine the boundaries of the packets, exactly how they were sent (if the packet made it over the network).

Upvotes: 1

Related Questions