Ethernet icon indicating copy to clipboard operation
Ethernet copied to clipboard

Add non-blocking alternative to EthernetClient::connect()

Open mghie opened this issue 2 years ago • 0 comments

I have a program which needs to always update outputs in a timely manner, even when calling EthernetClient::connect() (which can block for the number of milliseconds set with setConnectionTimeout()).

I have modified the current code:

int EthernetClient::connect(IPAddress ip, uint16_t port)
{
	if (_sockindex < MAX_SOCK_NUM) {
		if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) {
			Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()?
		}
		_sockindex = MAX_SOCK_NUM;
	}
#if defined(ESP8266) || defined(ESP32)
	if (ip == IPAddress((uint32_t)0) || ip == IPAddress(0xFFFFFFFFul)) return 0;
#else
	if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0;
#endif
	_sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
	if (_sockindex >= MAX_SOCK_NUM) return 0;
	Ethernet.socketConnect(_sockindex, rawIPAddress(ip), port);
	uint32_t start = millis();
	while (1) {
		uint8_t stat = Ethernet.socketStatus(_sockindex);
		if (stat == SnSR::ESTABLISHED) return 1;
		if (stat == SnSR::CLOSE_WAIT) return 1;
		if (stat == SnSR::CLOSED) return 0;
		if (millis() - start > _timeout) break;
		delay(1);
	}
	Ethernet.socketClose(_sockindex);
	_sockindex = MAX_SOCK_NUM;
	return 0;
}

to the following:

int EthernetClient::connect(IPAddress ip, uint16_t port)
{
	int ret = beginConnect(ip, port);
	if (ret >= 0) return ret;

	uint32_t start = millis();
	while (1) {
		ret = endConnect();
		if (ret >= 0) return ret;
		if (millis() - start > _timeout) break;
		delay(1);
	}
	Ethernet.socketClose(_sockindex);
	_sockindex = MAX_SOCK_NUM;
	return 0;
}

int EthernetClient::beginConnect(IPAddress ip, uint16_t port)
{
	if (_sockindex < MAX_SOCK_NUM) {
		if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) {
			Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()?
		}
		_sockindex = MAX_SOCK_NUM;
	}
	if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0;
	_sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
	if (_sockindex >= MAX_SOCK_NUM) return 0;
	Ethernet.socketConnect(_sockindex, rawIPAddress(ip), port);
	return -1;
}

int EthernetClient::endConnect()
{
	uint8_t stat = Ethernet.socketStatus(_sockindex);
	if (stat == SnSR::ESTABLISHED) return 1;
	if (stat == SnSR::CLOSE_WAIT) return 1;
	if (stat == SnSR::CLOSED) return 0;
	return -1;
}

Now instead of calling connect() I can call beginConnect() and then repeatedly call endConnect() during the main loop until it returns either 1 on success or 0 on failure.

If there is interest to include this into the library I can update the documentation (which is not up-to-date for connect() anyway) and create a PR. Please let me know.

mghie avatar May 01 '23 19:05 mghie