python udp broadcast sending but not receiving - python

I am trying a basic script that should be customized later on, but for now i need it to send a camera feed from a network connected raspberry Pi to multiple laptops on the same network.
I used udp streaming to a single device with the specific device address with the below code as a and it worked like a charm with no problem what so ever
sender
class FrameSegment():
""" this class inits the socket in the main file then sends the frames of a video in a loop with the udp_frame method """
MAX_IMAGE_DGRAM = 2**16 - 64 # minus 64 bytes in case UDP frame overflown
def __init__(self, port=5000):
self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # for linux use SO_REUSEPORT
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.s.settimeout(0.2)
self.PORT = port
def udp_frame(self, img):
compress_img = cv2.imencode(".jpg", img)[1]
dat = compress_img.tostring()
size = len(dat)
num_of_segments = math.ceil(size/(self.MAX_IMAGE_DGRAM))
array_pos_start = 0
while num_of_segments:
array_pos_end = min(size, array_pos_start + self.MAX_IMAGE_DGRAM)
msg = struct.pack("B", num_of_segments) + dat[array_pos_start:array_pos_end]
self.s.sendto(msg, ('192.168.1.110', self.PORT))
array_pos_start = array_pos_end
num_of_segments -= 1
Reciever
class RovCam():
""" inits the socket in main then reads the transmitted data from said socket in a loop to display the video """
MAX_DGRAM = 2**16 - 16
def __init__(self,port=5000):
self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # for linux use SO_REUSEPORT
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.s.bind(("", port))
self.dat = b''
self.dump_buffer()
print("ROV CAM : Connected successfully")
def dump_buffer(self):
while True:
seg, addr = self.s.recvfrom(self.MAX_DGRAM)
if struct.unpack("B", seg[0:1])[0] == 1:
break
def read(self):
seg, addr = self.s.recvfrom(self.MAX_DGRAM)
while struct.unpack("B", seg[0:1])[0] > 1:
self.dat += seg[1:]
seg, addr = self.s.recvfrom(self.MAX_DGRAM)
self.dat += seg[1:]
img = cv2.imdecode(np.fromstring(self.dat, dtype=np.uint8), 1)
self.dat = b''
return img
However The Problem is that if i change the address of the receiving device to a broadcast address like so
** in the sender file**
self.s.sendto(msg, ('192.168.1.255', self.PORT))
it stops working and the receiving device cannot read anything.
I then confirmed with the tcpdump tool that the receiver device is indeed receiving the sent stream over the specified port but the script has a hard time seeing that.

Turns out that it's a network error and when i removed the router from the network and made a smaller network with just a switch and an on board DHCP server on one of the devices used it handled the connection successfully

Related

How to connect to Python socket server from another network

I'm new to socket programming and I'm trying to make a server with socket module. When working on a local network it works perfectly fine, but the problem comes when I'm trying to run it and connect from two different networks. I've tried many things that I saw on SO and the internet in general.
Server code:
import socket
import threading
SERVER = socket.gethostbyname('')
PORT = 12345
ADDR = (SERVER, PORT)
HEADER = 64
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!DISCONNECT'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr):
print(f'[NEW CONNECTION] {addr} connected.')
print(f'[ACTIVE CONNECTIONS] {threading.active_count() - 1}')
connected = True
while connected:
msg_lenght = conn.recv(HEADER).decode(FORMAT)
if msg_lenght:
msg_lenght = int(msg_lenght)
msg = conn.recv(msg_lenght).decode(FORMAT)
if msg == DISCONNECT_MESSAGE:
connected = False
print(f'[MESSAGE RECIEVED] FROM: {addr}; Message: {msg}')
conn.send('Message received!'.encode(FORMAT))
conn.close()
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}')
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f'IP Address: {SERVER}')
print('[STARTING] Server is starting...')
start()
Client code:
import socket
SERVER = input('IP Address: ')
PORT = 5050
ADDR = (SERVER, PORT)
HEADER = 64
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!DISCONNECT'
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message = msg.encode(FORMAT)
msg_lenght = len(message)
send_lenght = str(msg_lenght).encode(FORMAT)
send_lenght += b' ' * (HEADER - len(send_lenght))
client.send(send_lenght)
client.send(message)
while True:
user_Input = input('Message: ')
send(user_Input)
if user_Input == DISCONNECT_MESSAGE:
break
I tried making "rules" on the firewall that should make my port public or something. I have also tried different addresses because I was not sure which one to use.
Thanks in advance!

Python socket hangs on connect/accept

I'm trying to send packets using sockets, and was able to do so just fine until this morning. I'm not sure what's going on. The packets are showing up in tcpdump but the server and the client cannot connect to each other.
netcat.py
import socket
import argparse
import sys
import os
import re
import threading
def convertContent(content: str = "") -> bytes:
byteContent = []
# grab the hex from the content
for i in range(len(content)):
if content[i] == "\\" and content[i+1] == "x":
byteContent.append(f"{content[i+2]}{content[i+3]}")
# grab the non hex from the content, split it on the hex
stringContent = re.split(r"\\x.{2}", content)
byteIndex = 0
newContent = b""
# Re add the non-hex content, and the hex content
for word in stringContent:
newContent += word.encode()
if byteIndex < len(byteContent):
newContent += bytes.fromhex(byteContent[byteIndex])
byteIndex += 1
newContent = newContent.replace(b"\\n", b"\n").replace(b"\\r", b"\r")
return newContent
class Netcat():
'''
Netcat class that can be used to send/receive TCP packets
'''
BUFFER_SIZE = 1024
def __init__(self):
pass
#classmethod
def createSocket(cls):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Port might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
return sock
#classmethod
def send(cls, hostname: str = "127.0.0.1", srcPort: int = 0, destPort: int = 9999, content: str = "", buffer_size: int = 1024):
srcPort = int(srcPort)
destPort = int(destPort)
try:
content = convertContent(content=content)
except:
pass
sock = cls.createSocket()
# Set the source port before sending
sock.connect((hostname, destPort))
sock.sendall(content)
# shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
sock.shutdown(socket.SHUT_WR)
while True:
data = sock.recv(buffer_size)
if len(data) == 0:
break
sock.close()
#classmethod
def receive(cls, port: int = 9999, buffer_size: int = 1024):
if port <= 1024 and os.geteuid() != 0:
print(f"Listening on port {port} requires superuser privileges!")
return
host = ""
sock = cls.createSocket()
sock.bind((host, port))
sock.listen(10)
conn, addr = sock.accept()
while True:
data = conn.recv(buffer_size)
if not data:
break
conn.close()
threading.Thread(target=Netcat.receive,daemon=True).start()
Netcat.send(content="test")
Note: I am sending the packets from one VM to another, rather than sending to myself, but it would be a lot to ask people to spin up a bunch of VMs to reproduce this. The hostname param in the send method should be the actual IP of the receiving machine
I've thrown some print statements, and the server stops on sock.accept(), while the client hangs on sock.connect((hostname, destPort))
I checked the hostname for the server, and it's listening on (0.0.0.0, 8888) (assuming 8888 is the port param), which means its listening on all interfaces on that port, so I dont know why its refusing to connect
I tcpdumped on the server, and its getting the packets, it gets a SYN, then sends out a SYN, ACK, but the rest of the packets are marked as re-transmissions.
I've tried looping the accept & connect lines, thinking maybe some sort of race condition was occurring, but no matter what I do the client can't connect to the server.
Edit: This works on my local machine, but still breaks when I try to send packets over the network. The first 2 steps of the handshake go through SYN & SYN, ACK, but not the third ACK
Don't bind in the client. Working example below, but minor changes to make a standalone script:
import socket
import threading
def receive(port: int = 9999, buffer_size: int = 1024):
host = ""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen()
conn, addr = sock.accept()
while True:
data = conn.recv(buffer_size)
if not data:
break
print(data)
conn.close()
def send(hostname: str = "127.0.0.1", destPort: int = 9999, content: str = b"test", buffer_size: int = 1024):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Address might be in a TIME_WAIT status, ignore this
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Removed bind
sock.connect((hostname, destPort))
sock.sendall(content)
# shutdown might be redundant/unnecessary (tells connected host that we're done sending data)
sock.shutdown(socket.SHUT_WR)
while True:
data = sock.recv(buffer_size)
if len(data) == 0:
break
sock.close()
threading.Thread(target=receive,daemon=True).start()
send()
Output:
b'test'

How can I bind more than one IP without using multithreading?

I'm wondering if it's possible to bind more than one multicast address without using multithreading(creating few sockets), just by creating one socket that can obtain data from two addresses (which is my current way of doing this).
My code looks like:
import socket
import struct
import time
MCAST_GRP2 = '239.0.1.105'
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True
#scan time in seconds
SCAN_TIME = 10
#sampling time in seconds
SAMPLING_TIME = 1
bufferUDP = 2048
totalSize = 0
bitrateList = []
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(("", MCAST_PORT))
sock2.bind(("", MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
sock2.bind((MCAST_GRP2, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
mreq2 = struct.pack("4sl", socket.inet_aton(MCAST_GRP2), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
sock2.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq2)
print("_____.:| Starting analysis of multicasts! |:._____\n")
for x in range(SCAN_TIME):
stop = time.time() + SAMPLING_TIME
while (time.time()<stop):
totalSize += len(sock.recv(bufferUDP)) + len(sock2.recv(bufferUDP))
bitrateList.append(totalSize)
print(bitrateList[x]*8/(1000000))
totalSize = 0
bitrateList.pop(0)
txtfile = open("Bitrate_history_ip_{}.txt".format("TESTTT"),"w+")
for x in range(SCAN_TIME-1):
bitrateList[x] = bitrateList[x]*8/(1000000)
txtfile.write("{}.Bitrate was equal to: {} Mbps\n".format(x+1,bitrateList[x]))
txtfile.write("Maximum bitrate value was: {} Mbps\n".format(max(bitrateList)))
txtfile.write("Minimum bitrate value was: {} Mbps\n".format(min(bitrateList)))
print('End of test')
time.sleep(5)
And is based on:
How do you UDP multicast in Python?
.. just by creating one socket that can obtain data from two addresses
One cannot bind a socket to multiple IP addresses.
One can still handle multiple sockets in parallel without needing multiple threads or multiple processes. This is done with an event-based architecture and non-blocking sockets, see Non-blocking Sockets in the Python documentation for more details.

Sending messages via TCP sockets

I am making a code that the server sends from 1 to 256 kbytes to client. The client receives the message and must return it to the server. The process must be repeated 1000 times. The message is read from a file. The server is sending and the client is picking it up and sending it back, but at a certain moment it stops. I'd like to know what's wrong with my code.
Server:
import socket
hostA = '127.0.0.1'
portA = 50031
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind((hostA, portA))
dest = ('127.0.0.1',50008)
arquivo = 'texto.txt'
arq = open(arquivo , 'r')
arq = arq.read()
for i in range(0, 9):
dado = arq[0:(1024 * (2**i))]
for j in range(0, 1000):
for k in range(0, (len(dado) / 1024)):
x = dado[k:k+1024]
udp.sendto(x, dest)
for k in range(0, (len(dado) / 1024)):
msg, cliente = udp.recvfrom(1024)
udp.close()
Client:
import socket
hostB = '127.0.0.1'
portB = 50008
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind((hostB, portB))
orig = ('127.0.0.1',50031)
dado = ""
for i in range(0, 9):
for j in range(0, 1000):
for l in range(0, ((1024 * (2**i))/1024)):
msg, cliente = udp.recvfrom(1024)
dado += msg
for k in range(0, ((1024 * (2**i))/1024)):
x = dado[k:k+1024]
udp.sendto(x, orig)
udp.close()
Your question asks about "TCP sockets", but you aren't using TCP.
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind((hostA, portA))
You are using UDP. Unlike TCP, UDP does not detect lost packets or retransmit or re-order data.
Your protocol has no tolerance for packet loss. If a single packet from either the server to the client or the client to the server is lost, each side will wait for the other forever.
You have other issues too. Imagine if two datagrams sent by the client are received in the wrong order by the server. The data will be corrupted.
If you want to design on top of UDP rather than TCP, you have to implement yourself everything TCP provides that you need. If you need lost datagram detection and retransmission, you need to implement it yourself or use TCP. Ditto for transmit pacing, handling out of order reception, and so on.

Python Networking Timestamp (SO_TIMESTAMP, SO_TIMESTAMPNS, SO_TIMESTAMPING)

Linux Kernel offers a few ways to get timestamps for received (SO_TIMESTAMP, SO_TIMESTAMPNS, SO_TIMESTAMPING) or sent (SO_TIMESTAMPING) packets.
Kernel Doc: https://www.kernel.org/doc/Documentation/networking/timestamping.txt
Is there a way I can use that with Python? I don't see any SO_TIMESTAMP constant inside the Python sources. Tried 3.6.2 and GitHub master branch.
Right now, I can only use SIOCGSTAMP that gives me the timestamp of the last received packet and nothing seems available for sent packet timestamp.
Finally, I have been able to get the SO_TIMESTAMPNS value like this:
SO_TIMESTAMPNS = 35
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, SO_TIMESTAMPNS, 1)
raw_data, ancdata, flags, address = s.recvmsg(65535, 1024)
ancdata[0][2] is the hardware timestamp as a timespec(ulong, ulong).
Does work on Linux but not on Mac OS X. Not tested on Windows.
complete code, send and receive using python3
import struct
import time
import select
import socket
import sys
if(len(sys.argv)!=2):
print("usage: ",sys.argv[0]," <send|receive>")
sys.exit()
print(sys.argv[1]);
if(sys.argv[1]=='send'):
MESSAGE = "Hello, World!"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
s.connect(('localhost', 10000))
s.send(MESSAGE)
time.sleep(0.0001)
#time.sleep(5)
s.send(MESSAGE)
s.close()
else:
SO_TIMESTAMPNS = 35
#s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.setsockopt(socket.SOL_SOCKET, SO_TIMESTAMPNS, 1)
server.bind(('localhost', 10000))
server.listen(5)
inputs = [ server ]
message_queues = {}
outputs = []
while inputs:
print('\nwaiting for the next event')
readable, writable, exceptional = select.select(inputs, outputs, inputs)
for s in readable:
if s is server:
connection, client_address = s.accept()
print('new connection from', client_address)
connection.setblocking(0)
inputs.append(connection)
else:
raw_data, ancdata, flags, address = s.recvmsg(65535, 1024)
print('received ', raw_data, '-',ancdata,'-',flags,'-',address)
if(len(ancdata)>0):
#print(len(ancdata),len(ancdata[0]),ancdata[0][0],ancdata[0][1],ancdata[0][2])
#print('ancdata[0][2]:',type(ancdata[0][2])," - ",ancdata[0][2], " - ",len(ancdata[0][2]));
for i in ancdata:
print('ancdata: (cmsg_level, cmsg_type, cmsg_data)=(',i[0],",",i[1],", (",len(i[2]),") ",i[2],")");
if(i[0]!=socket.SOL_SOCKET or i[1]!=SO_TIMESTAMPNS):
continue
tmp=(struct.unpack("iiii",i[2]))
timestamp = tmp[0] + tmp[2]*1e-10
print("SCM_TIMESTAMPNS,", tmp, ", timestamp=",timestamp)
if(not raw_data):
print('closing after reading no data')
# Stop listening for input on the connection
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()

Categories