diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index ca5ae0c..ba7f7d9 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -4,6 +4,7 @@ # SPDX-FileCopyrightText: 2018 Paul Stoffregen # SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries # SPDX-FileCopyrightText: 2021 Patrick Van Oosterwijck +# SPDX-FileCopyrightText: 2021 Adam Cummick # # SPDX-License-Identifier: MIT @@ -328,10 +329,13 @@ def link_status(self): return data[0] & 0x01 return 0 - @property - def remote_port(self): + def remote_port(self, socket_num): """Returns the port of the host who sent the current incoming packet.""" - return self.remote_port + if socket_num >= self.max_sockets: + return self._pbuff + for octet in range(0, 2): + self._pbuff[octet] = self._read_socket(socket_num, REG_SNDPORT + octet)[0] + return int((self._pbuff[0] << 8) | self._pbuff[0]) @property def ifconfig(self): @@ -598,6 +602,22 @@ def socket_listen(self, socket_num, port): if status[0] == SNSR_SOCK_CLOSED: raise RuntimeError("Listening socket closed.") + def socket_accept(self, socket_num): + """Gets the dest IP and port from an incoming connection. + Returns the next socket number so listening can continue + :parm int socket_num: socket number + """ + dest_ip = self.remote_ip(socket_num) + dest_port = self.remote_port(socket_num) + next_socknum = self.get_socket() + if self._debug: + print( + "* Dest is ({}, {}), Next listen socknum is #{}".format( + dest_ip, dest_port, next_socknum + ) + ) + return next_socknum, (dest_ip, dest_port) + def socket_open(self, socket_num, conn_mode=SNMR_TCP): """Opens a TCP or UDP socket. By default, we use 'conn_mode'=SNMR_TCP but we may also use SNMR_UDP. diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 4033659..eae8c12 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -9,7 +9,7 @@ A socket compatible interface with the Wiznet5k module. -* Author(s): ladyada, Brent Rubell, Patrick Van Oosterwijck +* Author(s): ladyada, Brent Rubell, Patrick Van Oosterwijck, Adam Cummick """ import gc @@ -108,11 +108,31 @@ def __init__( if self._socknum == SOCKET_INVALID: raise RuntimeError("Failed to allocate socket.") + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.disconnect() + stamp = time.monotonic() + while self.status == adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT: + if time.monotonic() - stamp > 1000: + raise RuntimeError("Failed to disconnect socket") + self.close() + stamp = time.monotonic() + while self.status != adafruit_wiznet5k.SNSR_SOCK_CLOSED: + if time.monotonic() - stamp > 1000: + raise RuntimeError("Failed to close socket") + @property def socknum(self): """Returns the socket object's socket number.""" return self._socknum + @property + def status(self): + """Returns the status of the socket""" + return _the_interface.socket_status(self.socknum)[0] + @property def connected(self): """Returns whether or not we are connected to the socket.""" @@ -147,10 +167,16 @@ def inet_aton(self, ip_string): return self._buffer def bind(self, address): - """Bind the socket to the listen port, we ignore the host. - :param tuple address: local socket as a (host, port) tuple, host is ignored. + """Bind the socket to the listen port, if host is specified the interface + will be reconfigured to that IP. + :param tuple address: local socket as a (host, port) tuple. """ - _, self._listen_port = address + if address[0] is not None: + ip_address = _the_interface.unpretty_ip(address[0]) + current_ip, subnet_mask, gw_addr, dns = _the_interface.ifconfig + if ip_address != current_ip: + _the_interface.ifconfig = (ip_address, subnet_mask, gw_addr, dns) + self._listen_port = address[1] def listen(self, backlog=None): """Listen on the port specified by bind. @@ -160,6 +186,35 @@ def listen(self, backlog=None): _the_interface.socket_listen(self.socknum, self._listen_port) self._buffer = b"" + def accept(self): + """Accept a connection. The socket must be bound to an address and listening for + connections. The return value is a pair (conn, address) where conn is a new + socket object usable to send and receive data on the connection, and address is + the address bound to the socket on the other end of the connection. + """ + stamp = time.monotonic() + while self.status not in ( + adafruit_wiznet5k.SNSR_SOCK_SYNRECV, + adafruit_wiznet5k.SNSR_SOCK_ESTABLISHED, + ): + if self._timeout > 0 and time.monotonic() - stamp > self._timeout: + return None + if self.status == adafruit_wiznet5k.SNSR_SOCK_CLOSED: + self.close() + self.listen() + + new_listen_socknum, addr = _the_interface.socket_accept(self.socknum) + current_socknum = self.socknum + # Create a new socket object and swap socket nums so we can continue listening + client_sock = socket() + client_sock._socknum = current_socknum # pylint: disable=protected-access + self._socknum = new_listen_socknum # pylint: disable=protected-access + self.bind((None, self._listen_port)) + self.listen() + while self.status != adafruit_wiznet5k.SNSR_SOCK_LISTEN: + raise RuntimeError("Failed to open new listening socket") + return client_sock, addr + def connect(self, address, conntype=None): """Connect to a remote socket at address. (The format of address depends on the address family — see above.) diff --git a/examples/wiznet5k_simpleserver.py b/examples/wiznet5k_simpleserver.py new file mode 100644 index 0000000..deb42fc --- /dev/null +++ b/examples/wiznet5k_simpleserver.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2021 Adam Cummick +# +# SPDX-License-Identifier: MIT + +import board +import busio +import digitalio +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket + +print("Wiznet5k SimpleServer Test") + +# For Adafruit Ethernet FeatherWing +cs = digitalio.DigitalInOut(board.D10) +# For Particle Ethernet FeatherWing +# cs = digitalio.DigitalInOut(board.D5) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface +eth = WIZNET5K(spi_bus, cs, is_dhcp=False) + +# Initialize a socket for our server +socket.set_interface(eth) +server = socket.socket() # Allocate socket for the server +server_ip = "192.168.10.1" # IP address of server +server_port = 50007 # Port to listen on +server.bind((server_ip, server_port)) # Bind to IP and Port +server.listen() # Begin listening for incoming clients + +while True: + conn, addr = server.accept() # Wait for a connection from a client. + with conn: + data = conn.recv() + print(data) + conn.send(data) # Echo message back to client