36

I'm new to python and sockets and am trying to write an echoing client/server socket. I have written the server so that 30% of the packets are lost. I programmed my client to timeout after one second since the packet could be lost. However, whenever I run my client socket, my output is 100% REQUEST TIMED OUT. I'm assuming I'm getting this output because my server is never receiving the message. I've looked over my code multiple times and cannot figure out why I am constantly getting this output. Below is my code for my server and client sockets. Any help would be appreciated.

Server Socket:

 # We will need the following module to generate randomized lost packets
    import random
    from socket import *

    # Create a UDP socket
    # Notice the use of SOCK_DGRAM for UDP packets
    serverSocket = socket(AF_INET, SOCK_DGRAM)

    # Assign IP address and port number to socket
    serverSocket.bind(('', 12000))

    while True:
        # Generate random number in the range of 0 to 10
        rand = random.randint(0, 10)

        # Receive the client packet along with the address it is coming from
        message, address = serverSocket.recvfrom(1024)

        # Capitalize the message from the client
        message = message.upper()

        # If rand is less is than 4, we consider the packet lost and do notrespond
        if rand < 4:
            continue

        # Otherwise, the server responds
        serverSocket.sendto(message, address) 

Client Socket:

import time
from socket import *

pings = 1

#Send ping 10 times 
while pings < 11:  

    #Create a UDP socket
    clientSocket = socket(AF_INET, SOCK_DGRAM)

    #Set a timeout value of 1 second
    clientSocket.settimeout(1)

    #Ping to server
    message = 'test'

    addr = ("127.0.0.1", 12000)

    #Send ping
    start = time.time()
    clientSocket.sendto(message, addr)

    #If data is received back from server, print 
    try:
        data, server = clientSocket.recvfrom(1024)
        end = time.time()
        elapsed = end - start
        print data + " " + pings + " "+ elapsed        

    #If data is not received back from server, print it has timed out  
    except timeout:
        print 'REQUEST TIMED OUT'

    pings = pings - 1
user3335367
  • 435
  • 3
  • 6
  • 9
  • 4
    If you came here from a Google search for how to implement UDP sockets in Python and wonder why some requests are failing, it's intentional. Read the post carefully. – rovyko Apr 17 '21 at 01:27
  • If you're using an AWS AMI, make sure you are using the PUBLIC ip not the PRIVATE ip. – P i Sep 04 '21 at 07:43
  • 1
    A minor point: for the sake of readability, avoid ```import *```. Either list the imported modules or functions explicitly or just import the base module and use the dot notation throughout your code; this way, future readers (including yourself!) will quickly be able to identify where things are coming from. @bsa provides other good suggestions to improve the quality of your code. – Ender Mar 20 '22 at 04:36

2 Answers2

59

I tested your code, and it works as expected on my machine. Your issue might not be your code. It could be a firewall or something else blocking all the packets on the loopback interface (127.0.0.1). Depending on your operating system, try testing with a packet monitor like Wireshark.

Also, here are a few suggestions on how to improve your code to be more Pythonic:

Server

import random
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('', 12000))

while True:
    rand = random.randint(0, 10)
    message, address = server_socket.recvfrom(1024)
    message = message.upper()
    if rand >= 4:
        server_socket.sendto(message, address)

Client

import time
import socket

for pings in range(10):
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client_socket.settimeout(1.0)
    message = b'test'
    addr = ("127.0.0.1", 12000)

    start = time.time()
    client_socket.sendto(message, addr)
    try:
        data, server = client_socket.recvfrom(1024)
        end = time.time()
        elapsed = end - start
        print(f'{data} {pings} {elapsed}')
    except socket.timeout:
        print('REQUEST TIMED OUT')
bsa
  • 2,671
  • 21
  • 31
  • 1
    Okay, that would explain things. I've stared at this things for hours and could not figure it out. Thank you very much, and thank you for the tips – user3335367 Jan 12 '15 at 02:01
  • In line `clientSocket.sendto(message, addr)` didn't you get error of `TypeError: a bytes-like object is required, not 'str'` – S Andrew Nov 22 '17 at 07:32
  • My guess would be that I used Python 2, you might be using Python 3? – bsa Nov 23 '17 at 08:27
  • @SAndrew I think in python2, string objects are compatible with bytes objects, whereas python3 makes the two explicitly separate. If you change the line `message = 'test'` to `message = b'test'` it should work for you. Also: 3rd Python is best Python. – ocket8888 Dec 14 '17 at 18:24
  • 2
    "more pythonic" you literally _just_ removed the excess newlines and comments, and changed one while-loop into a for loop – Vegard J. Jan 10 '18 at 07:22
  • 2
    Also deleted a `continue`, and used the string format operator instead of string concatenation :) What else would you change? – bsa Feb 27 '18 at 14:37
  • Not too sure if it matters, but obviously if rand is not >= 4, you get time outs as you are not sending any data back to the client. As you are getting 100% time outs, that's likely not due to the code unless you got extremely lucky with the randoms. – Fastidious May 13 '18 at 12:34
6

Here is an alternative with asyncio.

import asyncio
import random

class EchoServerProtocol:
    def connection_made(self, transport):
        self.transport = transport

    def datagram_received(self, data, addr):
        message = data.decode()
        print('Received %r from %s' % (message, addr))
        rand = random.randint(0, 10)
        if rand >= 4:
            print('Send %r to %s' % (message, addr))
            self.transport.sendto(data, addr)
        else:
            print('Send %r to %s' % (message, addr))
            self.transport.sendto(data, addr)


loop = asyncio.get_event_loop()
print("Starting UDP server")

# One protocol instance will be created to serve all client requests
listen = loop.create_datagram_endpoint(
    EchoServerProtocol, local_addr=('127.0.0.1', 12000))
transport, protocol = loop.run_until_complete(listen)

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

transport.close()
loop.close()
Fastidious
  • 1,249
  • 4
  • 25
  • 43
  • 7
    Could you please add a few lines to describe your code block. E.g. Why would anyone do it this way? Pros/Cons? Thanks for the example! – n1k31t4 May 25 '18 at 09:08
  • `if rand >= 4:` is same as in else block , so why you need to use if else at first place ? – Chang Zhao Jan 02 '20 at 11:06