Raspi Pico W [Errno 98] EADDRINUSE despite using socket.SO_REUSEADDR - python

I'm trying to set up a simple server/client connection using the socket module on a Raspberry Pi Pico W running the latest nightly build image rp2-pico-w-20221123-unstable-v1.19.1-713-g7fe7c55bb.uf2 which I've downloaded from https://micropython.org/download/rp2-pico-w/
The following code runs fine for the first connection (network connectivity can be assumed at this point).
import socket
def await_connection():
print(' >> Awaiting connection ...')
try:
host_addr = socket.getaddrinfo('0.0.0.0', 46321)[0][-1]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(host_addr)
sock.listen(1)
con, addr = sock.accept()
while True:
# keep receiving commands on the open connection until the client stops sending
cmd = con.recv(1024)
if not cmd:
print(f' >> {addr} disconnected')
break
elif cmd.decode() == 'foo':
response = 'bar'.encode()
else:
response = cmd
print(f"Received {cmd.decode()}")
print(f"Returning {response.decode()}")
con.sendall(response)
except OSError as e:
print(f' >> ERROR: {e}')
finally:
# appearantly, context managers are currently not supported in MicroPython, therefore the connection is closed manually
con.close()
print(' >> Connection closed.')
while True:
# main loop, causing the program to await a new connection as soon as the previous one is closed
await_connection()
If the client closes the connection and tries to re-connect, the infamous [Errno 98] EADDRINUSE is thrown:
Please note that I've already implemented the sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) statement, as recommended here, but to no avail.
However, if I run the exact same code on a Raspberry Pi 3 B with python 3.7.3 within the same network, everything works as expected - the client can disconnect and reconnect multiple times without issues:
How do I get the Pico to reuse the address after the initial connection, just like it is working in python 3.7.3?

While I was able to mitigate the reconnection crash by adding a sock.close() statement after the con.close(), the main issue with my code was the structure itself, as Steffen Ullrich pointed out.
The actual fix was to move the operations on the sock object out of the loop.
import socket
def await_connection():
print(' >> Awaiting connection ...')
try:
con, addr = sock.accept()
while True:
# keep receiving commands on the open connection until the client stops sending
cmd = con.recv(1024)
if not cmd:
print(f' >> {addr} disconnected')
break
else:
response = cmd
print(f"Received {cmd.decode()}")
print(f"Returning {response.decode()}")
con.sendall(response)
except OSError as e:
print(f' >> ERROR: {e}')
finally:
# appearantly, context managers are currently not supported in MicroPython, therefore the connection is closed manually
con.close()
print(' >> Connection closed.')
host_addr = socket.getaddrinfo('0.0.0.0', 46321)[0][-1]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(host_addr)
sock.listen(1)
while True:
# main loop, causing the program to await a new connection as soon as the previous one is closed
await_connection()

Related

How do I make my socket keep trying to connect even after the connection is closed?

So I am writing this program that will allow me to run commands from a different computer on the same network (my own version of ssh) in Python. I want the client program to run in the background of the target which I've already figured out the logistics to. What I would like to do is start the client program and never have to start it again but after I close the server program on the host computer, I get tons of errors. What I would like to do is after I close the host program, the client will continue to try to connect to the server program until I run it again. The code for my client program is here:
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
while True:
connect()
while True:
try:
msg = s.recv(1024)
os.system(msg.decode("utf-8"))
except:
s.connect('localhost', 1234)
The code for my host program is here:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 1234))
s.listen(5)
def main():
while True:
clientsocket, address = s.accept()
while address != None:
message = input("Enter Message: ")
messageb = message.encode("utf-8")
clientsocket.send(messageb)
main()
Note: I will change the address from localhost when I put this on a different computer.
Never do the following to start over a function without understanding the consequences of recursion. A function calling itself is called recursion and uses stack space. This will crash if the stack hits the recursion limit.
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
Here's a solution.
client.py:
import socket
import time
while True:
s = socket.socket()
try:
print('Trying to connect...')
s.connect(('localhost',8000))
print('Connected.')
try:
while True:
data = s.recv(1024)
if not data: break # server closed connection if nothing received.
print(data)
finally:
s.close()
print('Disconnected.')
except ConnectionError: # Any type of connection error, e.g. refused, aborted, reset.
time.sleep(1)
server.py:
import socket
import time
s = socket.socket()
s.bind(('',8000))
s.listen()
while True:
c,a = s.accept()
print(f'Connected: {a}')
try:
while True:
c.send(b'message')
time.sleep(1)
except ConnectionError:
c.close()
print(f'Disconnected: {a}')
Note also that TCP is a streaming protocol. It has no concept of messages. Take the time.sleep(1) out and the messages will all run together. A proper implementation will have a protocol to extract complete messages from the byte stream such as fixed sized messages, size transmitted followed by message, delimiter bytes like newlines between messages, etc.

Socket data not getting received when the request is made from a raspberry pi

I am trying to send and receive data from a server. When I run the below python program from eclipse or python IDLE I get the response from the server. But, when I run the program from Raspberry Pi I am getting an error. Below is the simple code and error.
import socket,time
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = '127.0.0.1'
port = 1883
address = (ip,port)
client.connect(address)
print("connected")
def close():
global client
client.close()
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(address)
while 1:
client.send(b'\x01\x04\x00\x00\xxxxxx')
print("sent")
try:
raw_data = client.recv(1024)
except:
close()
raw_data = client.recv(1024)
received_data = " ".join("{:02x}".format(byte) for byte in
(raw_data))
print(received_data)
time.sleep(1)
When I run this code in raspberry pi,I get BrokenPipe error. why it is happening in raspberry pi?
Error:
connected
sent
received
sent
Traceback (most recent call last):
File "exdu.py", line 18, in <module>
client.send(b'\x01\x04\x00\x00\xxxxxxxxx')
socket.error: [Errno 32] Broken pipe
A broken pipe is caused when a pipe (in this case a TCP socket) is closed unexpectedly by the other side and recv gets back an empty response or send tries to write to a closed connection.
There are quite a couple pieces of not so best practice code may be causing a broken pipe error in your code.
I'll try and point out a few things that stand out to me using comments:
import socket,time
# it would be nice to have a function wrapping this but as it is this is not the issue.
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = '127.0.0.1'
port = 1883
address = (ip,port)
client.connect(address)
print("connected")
def close():
global client
# If you read the python socket documentation https://docs.python.org/2/howto/sockets.html
# you will see that they advise sending a shutdown packet to the other side of the connection.
# In my experience this is actually extremely important when properly handling TCP connections and may be causing this issue.
# you must do this from the side that closes the connection, this includes when the server closes a connection.
client.shutdown(SHUT_RDWR) # this is what you want to do here.
client.close()
# connecting to a new socket here makes no sense and may also cause an issue.
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(address)
raw_data=client.recv(1024)
while 1:
client.send(b'\x01\x04\x00\x00\xxxxxx')
print("sent")
try:
raw_data = client.recv(1024)
except:
# this except statement may cause an exception itself.
# Not only that but you are probably getting your broken pipe error
# here from the recv in close().
close()
raw_data = client.recv(1024)
received_data = " ".join("{:02x}".format(byte) for byte in
(raw_data))
print(received_data)
time.sleep(1)
Apologies for the code comments, but i find they're useful when the segment is copy pasted when you are trying to experiment with a solution.
As an extension i would write the code you have to be much more like the python docs example:
class mysocket:
def __init__(self, sock=None):
if sock is None:
self.sock = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
else:
self.sock = sock
def connect(self, host, port):
self.sock.connect((host, port))
# important bit of missing code
def disconnect(self):
self.sock.shutdown(SHUT_RDWR)
self.sock.close()
def mysend(self, msg):
totalsent = 0
while totalsent < MSGLEN:
sent = self.sock.send(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
def myreceive(self):
chunks = []
bytes_recd = 0
while bytes_recd < MSGLEN:
chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
if chunk == '':
raise RuntimeError("socket connection broken")
chunks.append(chunk)
bytes_recd = bytes_recd + len(chunk)
return ''.join(chunks)
You can rewrite the send and recieve functions to do your bidding and then call it like:
socket = mysocket()
socket.connect('127.0.0.1', 1883)
try:
socket.mysend(b'\x01\x04\x00\x00\xxxxxx')
raw_data = socket.myrecieve()
# do more stuff
except:
socket.disconnect()
This is example code but may set you on the right path.
Broken pipe implies something isn't going through exactly as you want it to. Are you sure the server is running correctly on the raspberry pi on localhost?
Otherwise you might want to either run the server seperately(maybe heroku or digitalocean is relevant) or check your pi's firewall(I highly doubt this is the problem for localhost -> localhost connections)

python3 - broken pipe error when using socket.send()

I am programming a client-server instant message program. I created a similar program in Python 2, and am trying to program it in Python 3. The problem is when the server takes the message and tries to send it to the other client, it gives me "[Errno 32] Broken Pipe" and exits.
I have done some research, and found that this occurs when the client disconnects, so I did some more testing but could not find when the client disconnects. (I am using Ubuntu 14.04 and Python 3.4)
Here is the server code:
import socket, select, sys
def broadcast(sock, messaged):
for socket in connection_list:
if socket != s and socket != sock:
# Here is where it gives me the broken pipe error
try:
s.send(messaged.encode("utf-8"))
except BrokenPipeError as e:
print(e)
sys.exit()
connection_list = []
host = ''
port = 5558
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(5)
connection_list.append(s)
read_sockets,write_sockets,error_sockets = select.select(connection_list,[],[])
while True:
for sock in read_sockets:
if sock == s:
conn, addr = s.accept()
connection_list.append(conn)
client = "Client (%s,%s) connected" % addr
print(client)
broadcast(sock,client)
else:
try:
data = sock.recv(2048)
decodeddata = data.decode("utf-8")
if data:
broadcast(sock, decodeddata)
except:
offline = "Client " + addr + "is offline"
broadcast(sock, offline)
print(offline)
connection_list.remove(sock)
sock.close()
continue
And the client code:
import socket, select, string, sys, time
def prompt(data) :
print("<You> " + data)
def Person(data) :
print("<Receiver> " + data)
if __name__ == "__main__":
host = "localhost"
port = 5558
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
try:
s.connect((host,port))
except:
print('Unable to connect')
sys.exit()
print('Connected.')
socket_list = [s]
read_sockets,write_sockets,error_sockets = select.select(socket_list,[],[])
while 1:
for sock in read_sockets:
if sock == s:
try:
time.sleep(1)
data = sock.recv(1024)
Person(data.decode("utf-8"))
except:
msg = input("Send a message: ")
try:
s.send(str.encode(msg))
except:
print("Server is offline")
sys.exit()
else:
print("Server is offline")
sys.exit()
There are two problems that you have to fix to make this work.
First, on both the client side and the server side, you have to put the select inside the loop, not outside. Otherwise, if there was something to read before you got to the loop, you'll recv over and over, and if there wasn't, you'll never recv. Once you fix this, you can get rid of the time.sleep(1). (You should never need a sleep to solve a problem like this; at best it masks the problem, and usually introduces new ones.)
Meanwhile, on the server side, inside broadcast, you're doing s.send. But s is your listener socket, not a connected client socket. You want socket.send here, because socket is each socket in connection_list.
There are a number of unrelated problems in your code as well. For example:
I'm not sure what the except: in the client is supposed to be catching. What it mainly seems to catch is that, about 50% of the time, hitting ^C to end the program triggers the send prompt. But of course, like any bare except:, it also masks any other problems with your code.
There's no way to send any data back and forth other than the "connected" message except for that except: clause.
addr is a tuple of host and port, so when someone goes offline, the server raises a TypeError from trying to format the offline message.
addr is always the last client who connected, not the one who's disconnecting.
You're not setting your sockets to nonblocking mode.
You're not checking for EOF on the recv. This means that you don't actually detect that a client has gone offline until you get an error. Which normally happens only after you try to send them a message (e.g., because someone else has connected or disconnected).

error when trying to connect to server for next the connection using sockets in python

I am learning socket programming using python. my first assignment is to a write a client.py and a server.py. The client sends a message to server. The server receives the message of 16 bytes each time. After it has received the entire message, it will send the same message back to client.
so it is very simple. The server has backlog of 1. After the server sends the message to client, the connection to client close and the server should be open to receive new connection.
my current code fails in the last step. It is not open to receive new connections. It is throwing error. I even figured out the error. but I do not know how to fix this.
The error comes from server.py because I call for sock.accept() but I have closed the sock.
Let me explain my server.py code: I have two while loops. The outer loop looks for new connection, and the inner loop looks process request from connections i.e it simply receives data, wait till everything is received and send it back to client and finally close the connection.
I am asked not to change the structure of two while loops but just implement them.
Any thoughts or ideas on this:
client.py
import socket
import sys
def client(msg, log_buffer=sys.stderr):
server_address = ('localhost', 10000)
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP)
sock.connect(server_address)
print >>log_buffer, 'connecting to {0} port {1}'.format(*server_address)
try:
print >>log_buffer, 'sending "{0}"'.format(msg)
sock.sendall(msg)
chunk = ''
done=False;
while not done:
chunk+=sock.recv(16)
if chunk==msg:
done=True
print >>log_buffer, 'received "{0}"'.format(chunk)
finally:
print >>log_buffer, 'closing socket'
sock.close()
if __name__ == '__main__':
if len(sys.argv) != 2:
usg = '\nusage: python echo_client.py "this is my message"\n'
print >>sys.stderr, usg
sys.exit(1)
msg = sys.argv[1]
client(msg)
Server.py
import socket
import sys
def server(log_buffer=sys.stderr):
# set an address for our server
address = ('127.0.0.1', 10000)
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# log that we are building a server
print >>log_buffer, "making a server on {0}:{1}".format(*address)
sock.bind(address)
sock.listen(1)
try:
# the outer loop controls the creation of new connection sockets. The
# server will handle each incoming connection one at a time.
while True:
print >>log_buffer, 'waiting for a connection'
conn,add=sock.accept()
addr=(conn,add)
try:
print >>log_buffer, 'connection - {0}:{1}'.format(*addr)
# the inner loop will receive messages sent by the client in
# buffers. When a complete message has been received, the
# loop will exit
data = ''
while True:
recvdata=conn.recv(16)
print recvdata
data+=recvdata
print >>log_buffer, 'received "{0}"'.format(data)
print >>log_buffer, "len of received data: {0}".format(len(recvdata))
if len(recvdata)<16:
print >>log_buffer,"sending data"
conn.sendall(data)
break
conn.close()
finally:
sock.close()
except KeyboardInterrupt:
sock.close()
if __name__ == '__main__':
server()
sys.exit(0)
I runpython server.py in one terminal andpython client.py "This is the first message. send me back"` in a different terminal. The client connection is lost normally as expected. But I get the following error at server side (towards the end):
making a server on 127.0.0.1:10000
waiting for a connection
connection - <socket._socketobject object at 0x100849c20>:('127.0.0.1', 50626)
sairam hopefully
received "sairam hopefully"
len of received data: 16
this works lets
received "sairam hopefully this works lets"
len of received data: 16
c
received "sairam hopefully this works lets c"
len of received data: 2
sending data
waiting for a connection
Traceback (most recent call last):
File "echo_server.py", line 89, in <module>
server()
File "echo_server.py", line 39, in server
conn,add=sock.accept()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 202, in accept
sock, addr = self._sock.accept()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 170, in _dummy
raise error(EBADF, 'Bad file descriptor')
socket.error: [Errno 9] Bad file descriptor
You are closing sock inside your while loop. Don't do that. sock is your long-lasting server socket, which needs to remain open to listen for new connections. conn is your ephemeral socket, which needs to remain open only the length of a single connection.
Close conn after each connection, close sock when the server needs to terminate.
More simply, replace these lines:
finally:
sock.close()
with
finally:
conn.close()
What you are trying to do is a simple echo server, which I believe you can implement much more simply.
Server:
import socket
host = ''
port = 50000
backlog = 5
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(backlog)
while 1:
client, address = s.accept()
data = client.recv(size)
if data:
client.send(data)
client.close()
Client:
import socket
host = 'localhost'
port = 50000
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send('Hello, world')
data = s.recv(size)
s.close()
print 'Received:', data

Run a python socket server and if closed remotely start it listening again

I am struggling to get my python socket to behave.
There are two major problems:
1) When it listens for the client connection the program stalls which is a problem because it is running on a IRC client python interpreter causing the IRC client not to respond until the client connects.
2) When the client disconnects the entire script has to be stopped and then restarted again inorder to get the socket server to listen once more.
I thought the way around it might be to start the socket listening in a separate thread, so the IRC client can continue while it waits for the client connection. Also, once the client has decided to close the connection I need a way restart it.
The following code is terrible and doesn't work but it might give you an idea as to what I'm attempting:
__module_name__ = "Forward Module"
__module_version__ = "1.0.0"
__module_description__ = "Forward To Flash Module by Xcom"
# Echo client program
import socket
import sys
import xchat
import thread
import time
HOST = None # Symbolic name meaning all available interfaces
PORT = 7001 # Arbitrary non-privileged port
s = None
socketIsOpen = False
def openSocket():
# start server
print "starting to listen"
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,
socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error as msg:
s = None
continue
try:
s.bind(sa)
s.listen(1)
except socket.error as msg:
s.close()
s = None
continue
break
if s is None:
print 'could not open socket'
global socketIsOpen = False
sys.exit(1)
conn, addr = s.accept()
print 'Connected by', addr
global socketIsOpen = True
def someone_said(word, word_eol, userdata):
username = str(word[0])
message = str(word[1])
sendMessage = username + " : " + message
send_to_server(sendMessage)
def send_to_server(message):
conn.send(message)
def close_connection():
conn.close()
print "connection closed"
xchat.hook_print('Channel Message' , someone_said)
def threadMethod(arg) :
while 1:
if (not socketIsOpen) :
openSocket()
try:
thread.start_new_thread(threadMethod, args = [])
except:
print "Error: unable to start thread"
The python is running on an IRC client called HexChat which is where the xchat import comes from.
The way you usually program a threaded socket server is:
call accept() in a loop
spawn a new thread to handle the new connection
A very minimal example would be somethig like this:
import socket
import threading
import time
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 9999))
server.listen(1)
def handle(conn):
conn.send(b'hello')
time.sleep(1) # do some "heavy" work
conn.close()
while True:
print('listening...')
conn, addr = server.accept()
print('handling connection from %s' % (addr,))
threading.Thread(target=handle, args=(conn,)).start()
You're spawning new threads in which you create your listening socket, then accept and handle your connection. And while socketIsOpen is True your programm will be using a lot of cpu looping through your while loop doing nothing. (btw, the way you check socketIsOpen allows for race conditions, you can start multiple threads before it is set.)
And one last thing, you should try to use the threading module instead of the deprecated thread.

Categories