Skip to content

Commit

Permalink
Add ability to create an EthernetServer without setting the port
Browse files Browse the repository at this point in the history
  • Loading branch information
ssilverman committed Jul 23, 2022
1 parent 0694d40 commit 96f10f9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 29 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@ and this project adheres to
* New sections in the README:
* "How to change the number of sockets", and
* "On connections that hang around after cable disconnect".
* An `EthernetServer` instance can now be created without setting the port.
There are two new `begin()` functions for setting the port:
* `begin(port)`
* `begin(port, reuse)`

### Changed
* Moved CRC-32 lookup table to PROGMEM.
* Changed in `EthernetServer`:
* `port()` returns an `int32_t` instead of a `uint16_t` so that -1 can
represent an unset port; non-negative values are still 16-bit quantities
* `begin(reuse)` now returns a `bool` instead of `void`, indicating success
* The `EthernetServer` destructor now calls `end()`

### Removed
* Removed some unneeded network interfaces:
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,20 @@ The `Ethernet` object is the main Ethernet interface.
### `EthernetServer`

* `begin(reuse)`: Similar to `begin()`, but the Boolean `reuse` parameter
controls the SO_REUSEADDR socket option.
* `end()`: Shuts down the server.
* `port()`: Returns the server's port.
controls the SO_REUSEADDR socket option. This returns whether the server was
successfully started.
* `begin(port)`: Starts the server on the given port, first disconnecting any
existing server if it was listening on a different port. This returns whether
the server was successfully started.
* `begin(port, reuse)`: Similar to `begin(port)`, but `reuse` controls the
SO_REUSEADDR socket option. This returns whether the server was
successfully started.
* `end()`: Shuts down the server and returns whether it was stopped.
* `port()`: Returns the server's port, a signed 32-bit value, where -1 means the
port is not set and a non-negative value is a 16-bit quantity.
* `static constexpr int maxListeners()`: Returns the maximum number of
TCP listeners.
* `EthernetServer()`: Creates a placeholder server without a port.

### `EthernetUDP`

Expand Down
2 changes: 1 addition & 1 deletion examples/IPerfServer/IPerfServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ void addressChanged(bool hasIP) {
// Optional
printf("Address changed: Server already started\n");
} else {
printf("Starting server on port %u...", server.port());
printf("Starting server on port %u...", kServerPort);
fflush(stdout); // Print what we have so far if line buffered
server.begin();
printf("%s\n", server ? "done." : "FAILED!");
Expand Down
2 changes: 1 addition & 1 deletion examples/ServerWithListeners/ServerWithListeners.ino
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ void tellServer(bool hasIP) {
// Optional
printf("Address changed: Server already started\n");
} else {
printf("Starting server on port %u...", server.port());
printf("Starting server on port %u...", kServerPort);
fflush(stdout); // Print what we have so far if line buffered
server.begin();
printf("%s\n", server ? "done." : "FAILED!");
Expand Down
70 changes: 57 additions & 13 deletions src/QNEthernetServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,65 +16,109 @@
namespace qindesign {
namespace network {

EthernetServer::EthernetServer()
: port_(-1),
listening_(false) {}

EthernetServer::EthernetServer(uint16_t port)
: port_(port),
listening_(false) {}

EthernetServer::~EthernetServer() {
end();
}

void EthernetServer::begin() {
begin(false);
}

void EthernetServer::begin(bool reuse) {
bool EthernetServer::begin(bool reuse) {
if (port_ < 0) {
return false;
}
return begin(port_, reuse);
}

bool EthernetServer::begin(uint16_t port) {
return begin(port, false);
}

bool EthernetServer::begin(uint16_t port, bool reuse) {
if (port_ >= 0 && port_ != port) {
end(); // TODO: Should we call end() only if the new begin is successful?
}
port_ = port;
listening_ = internal::ConnectionManager::instance().listen(port_, reuse);
return listening_;
}

bool EthernetServer::end() const {
return internal::ConnectionManager::instance().stopListening(port_);
bool EthernetServer::end() {
if (port_ < 0) {
return true;
}
if (internal::ConnectionManager::instance().stopListening(port_)) {
listening_ = false;
return true;
}
return false;
}

EthernetClient EthernetServer::accept() const {
auto conn = internal::ConnectionManager::instance().findConnected(port_);
EthernetClass::loop();
if (conn != nullptr) {
internal::ConnectionManager::instance().remove(conn);
return EthernetClient{conn};
if (port_ >= 0) {
auto conn = internal::ConnectionManager::instance().findConnected(port_);
EthernetClass::loop();
if (conn != nullptr) {
internal::ConnectionManager::instance().remove(conn);
return EthernetClient{conn};
}
}
return EthernetClient{};
}

EthernetClient EthernetServer::available() const {
auto conn = internal::ConnectionManager::instance().findAvailable(port_);
EthernetClass::loop();
if (conn != nullptr) {
return EthernetClient{conn};
if (port_ >= 0) {
auto conn = internal::ConnectionManager::instance().findAvailable(port_);
EthernetClass::loop();
if (conn != nullptr) {
return EthernetClient{conn};
}
}
return EthernetClient{};
}

EthernetServer::operator bool() {
if (!listening_) {
if (!listening_ || port_ < 0) {
return false;
}
listening_ = internal::ConnectionManager::instance().isListening(port_);
return listening_;
}

size_t EthernetServer::write(uint8_t b) {
if (port_ < 0) {
return 1;
}
return internal::ConnectionManager::instance().write(port_, b);
}

size_t EthernetServer::write(const uint8_t *buffer, size_t size) {
if (port_ < 0) {
return size;
}
return internal::ConnectionManager::instance().write(port_, buffer, size);
}

int EthernetServer::availableForWrite() {
if (port_ < 0) {
return 0;
}
return internal::ConnectionManager::instance().availableForWrite(port_);
}

void EthernetServer::flush() {
if (port_ < 0) {
return;
}
internal::ConnectionManager::instance().flush(port_);
}

Expand Down
60 changes: 49 additions & 11 deletions src/QNEthernetServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#ifndef QNE_ETHERNETSERVER_H_
#define QNE_ETHERNETSERVER_H_

// C++ includes
#include <cstdint>

#include <Print.h>
#include <Server.h>

Expand All @@ -18,47 +21,82 @@ namespace network {

class EthernetServer final : public Server {
public:
EthernetServer();
EthernetServer(uint16_t port);

~EthernetServer();

// Returns the maximum number of TCP listeners.
static constexpr int maxListeners() {
return MEMP_NUM_TCP_PCB_LISTEN;
}

// Returns the port.
uint16_t port() const {
// Returns the server port. This will return -1 if it has not been set.
//
// Note that the value is still a 16-bit quantity if it is non-negative.
int32_t port() const {
return port_;
}

// Starts listening on the server port. This calls begin(false).
// Starts listening on the server port, if set. This calls begin(false).
void begin() override;

// Starts listening on the server port and sets the SO_REUSEADDR socket option
// according to the `reuse` parameter.
void begin(bool reuse);

bool end() const;

// Starts listening on the server port, if set, and sets the SO_REUSEADDR
// socket option according to the `reuse` parameter. This returns whether the
// server started listening. This will always return false if the port is
// not set.
bool begin(bool reuse);

// Starts listening on the specified port. This calls begin(port, false).
//
// If the port is already set and the server is listening, then it is first
// stopped with a call to end().
bool begin(uint16_t port);

// Starts listening on the specified port, if set, and sets the SO_REUSEADDR
// socket option according to the `reuse` parameter. This returns whether the
// server started listening.
//
// If the port is already set and the server is listening, then it is first
// stopped with a call to end(). This is to prevent a single server object
// from representing more than one listening socket.
bool begin(uint16_t port, bool reuse);

// Stops listening and returns whether the server is stopped. This will always
// return true if the port is not set.
bool end();

// Accepts a connection and returns a client, possibly unconnected. This
// returns an unconnected client if the port is not set.
EthernetClient accept() const;

// Finds a connection with available data. This returns an unconnected client
// if there is no client with available data or if the port is not set.
EthernetClient available() const;

// Bring Print::write functions into scope
using Print::write;

// Writes a byte to all the connections. This does nothing and returns 1 if
// the port is not set.
size_t write(uint8_t b) override;

// Writes data to all the connections. This does nothing and returns size if
// the port is not set.
size_t write(const uint8_t *buffer, size_t size) override;

// Returns the minimum availability of all the connections, or zero if there
// are no connections.
// are no connections or if the port is not set.
int availableForWrite() override;

// Flushes all the connections, but does nothing is the port is not set.
void flush() override;

operator bool();

private:
const uint16_t port_;
int32_t port_; // We also want to be able to represent a signed value
// Non-negative is 16 bits
bool listening_;
};

Expand Down

0 comments on commit 96f10f9

Please sign in to comment.