-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crosstalk between WiFi clients. Multiple clients assigned same socket number. #176
Comments
I confirm this crosstalk. I have debugged my problem with webserver and MQTT client running at same time. The fault is exactly that webserver is using client sockets that it does not own. The scenario is that a socket is used for webserver client in This section of code in // check previous received client socket
if (_lastSock != NO_SOCKET_AVAIL) {
WiFiClient client(_lastSock);
if (client.connected() && client.available()) {
sock = _lastSock;
}
} If the desire was to reuse open sockets then some alternative correct arrangement should be devised. I would also like to be able to close the listening server socket so please also provide an void WiFiServer2::end() {
if (_sock == NO_SOCKET_AVAIL)
return;
ServerDrv::stopClient(_sock);
int count = 0;
// wait maximum 5 secs for the connection to close
while (status() != CLOSED && ++count < 50)
delay(100);
WiFiSocketBuffer.close(_sock);
_sock = NO_SOCKET_AVAIL;
//stop remembering the last client, hopefully someone else will close this guy.
_lastSock = NO_SOCKET_AVAIL;
} |
Probably related to #87, also see https://github.com/ocrdu/WiFiNINA. |
I see this problem extensively in a MQTT broker I implemented on a MKR 1010. Any time a WiFiClient is kept open for an incoming connection past the next call to WiFiServer,available() there's a chance it gets returned as a new connection and you end up with two WiFiClients for the same socket. It's as if WiFiServer::available() was trying to serve as both an accept and a socket polling mechanism, but that only works if a single client were to be open as a time (as in the example code). While the above suggested fix of deleting the whole _lastSock mechanism suggested by learnfromgirls seems spot on, and should be done, I was able to work around the issue with the following: Keeping a side table of open clients based on a tuple of remote IP addr and TCP port. When calling WiFiServer::available(), and getting a client back, check its addr and port against the side table. If there's a match, ignore the available result and loop back to servicing clients. If whatever client has the _lastsock socket is empty of data when the available call is done, you get a shot at getting an actual new client back. It's not a complete workaround though as continuous traffic to the _lastsock client could create starvation for new incoming connections, but it might help people get past this until a fix is made. |
@LisaRowell do you know server.accept()? I think your problem is that you don't understand how server.available() works and use it wrong. |
I do know about server.accept() and would use it as a workaround if it were available. If server.available() was designed to return both new and existing socks then it is doing it wrong and will starve socks. If it's designed to just return new socks, then it is also doing that wrong as it returns the same twice. Either way you're a close away from bad things and with the released code there isn't a clean way of implementing a server that accepts multiple persistent client connections. |
@LisaRowell With server.available() you have to let the server manage the clients and not store the returned objects. You can differentiate the returned clients by remoteIP and remotePort if you have to (even then you will not know about a reconnect from the same IP and port). The sockets are freed when the client disconnects or when you decide to stop() it in your sketch. |
I understand what you are saying, however, at for me it doesn't work that way. We might have different firmware, I'm running stock 1.5.0. I instrument up WiFiServer to keep a history of what socks it gets back from The thing I find found that looks odd to me is that
When I close the client, things get cleared up somehow and it's able to reopen and available will service it. Any ideas what to look at from here? Thanks. |
I try to replicate your problem with my PagerServer example. But it works without a problem. What is the difference? |
I'm still trying to figure that what's significant and what's not. I'm potentially sending more data with multiple writes happening with no I've found that I only need two clients in my setup to cause a failure and if I comment out the line that sets I'm continuing to try and come up with a simple way of creating the issue. |
Hmmm if you insert calls to I'm looking at the code for that, and of course this is new to me code, but it looks like Since this was triggered by a I think, and you know this code way better than I do, that this is why Does this make sense? Again appreciate your help on all this. If I'm correct, should this be split to a different issue? |
I removed my check to WiFiClient::connected() and it definitely seems to get around the problem. |
but WiFiServer in the WiFiNINA library will not return any other client until there is buffered data in the _lastSock client. |
Until |
how? |
Let me back up and try and explain it another way. In your simple example you had a server accepting client connections and taking their input and doing a wall type broadcast to send the input to all connected clients. A different type of application might have to send different data, perhaps a subset, to each client, so it maintains state on a client basis and sends out info periodically that's relevant for each client. Follow me? Now, since the application is holding state for a client, it has to deal with when the client disconnects, cleaning up that state and ceasing to try to send it data. Because of how TCP works, and I can explain this in painstaking detail if you want but it's not super important, a disconnection happens without data being transmitted at the application level. This means that the application needs to somehow figure out if the connection is still alive. The mechanism to do that in this API is The not obvious thing here is that Understand still? There are two places data can be, on the NINA and locally in If you now look at The next thing It's a definite issue. |
sorry, but any WiFiClient returned by server.available() is invalid as soon as a new server.available() call is invoked. you need server.accept() |
As I said, I already have a workaround for this and have long since moved on. I was taking the time to explain to you that hoping you could understand the issue and fix it. It is not a complex change. As for server.accept() as being the answer, that commit is coming up on it's two year anniversary? |
I observed crosstalk between clients. For example, a response to a local FTP command was seen on a client doing an HTTP GET.
This crosstalk occurred after a loss of WiFi and subsequent reconnection.
I have limited understanding of the WiFiNINA software and even less understanding of the associated firmware. In reviewing that code, it seemed that the observed crosstalk could happen if multiple WiFi clients were using the same socket to communicate with the WiFi module. The sketch below shows that duplicate assignment of sockets can occur.
This sketch was executed on a MKR WiFi 1010 using version 1.8.8 of the WiFiNINA library. To see what sockets were being assigned the "_sock" variable in WiFiClient.h was changed from private to public.
Here's an output from the sketch.
Note that at the end both gclient and yclient are using socket 0.
It seems that the WiFiNINA library and the NINA firmware get out of sync on socket assignments.
A partial sequence of events:
restarting WiFi clears all socket assignments;
reconnecting 'yclient' assigns socket 0 to 'gclient'; and
reconnecting 'gclient' results in duplicate assignment of socket 0 because
WiFiClient::connect calls WiFiClient::stop because gclient._sock is 0 indicating that socket 0 is still assigned to 'gclient',
WiFiClient::stop clears the assignment of socket 0, and
socket 0 then gets assigned to 'gclient'.
Any suggestions on how to prevent this issue would be appreciated.
The text was updated successfully, but these errors were encountered: