Limiting Ethernet to one inbound connection

Hello,

I'm creating a device that will only be able to service one connection at a time. I want to respond to any attempt to connect beyond the first connection with the text "too many connections" and then drop the socket. How can I do this? I'm well aware that the Ethernet library will have to be hacked to accomplish this.

First you have to change the method available() of the EthernetServer class to return anything else but the first connected socket found, else you won't be able to send a string to that client. Then you have to program your sketch that way that after a valid socket is returned every other socket is immediately answered with your error string. If the error string should be sent without the client sending any characters to the server you have to add a connected method with about that content:

EthernetClient EthernetServer::connected()
{
  accept();

  for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port &&
         (client.status() == SnSR::ESTABLISHED ||
         (client.status() == SnSR::CLOSE_WAIT && client.available()))) {
      return client;
    }
  }

  return EthernetClient(MAX_SOCK_NUM);
}

Here the random socket selection is still missing (as described above for the available() method).

Seems to me this Ethernet library is coded the worst way possible, returning back a new EthernetClient each time connected() or available() get called. I would think it'd be a lot more efficient to have four instances of EthernetClient and manage their state independently. As it is, there's no way for EthernetServer to know there's an existing connection already established.

As it is, there's no way for EthernetServer to know there's an existing connection already established.

As it should be. Client/server connections are not meant to be persistent. The client asks for information. The server gives that information and then forgets all about having ever heard from that client.

PaulS:

As it is, there's no way for EthernetServer to know there's an existing connection already established.

As it should be. Client/server connections are not meant to be persistent. The client asks for information. The server gives that information and then forgets all about having ever heard from that client.

I've been doing this a long, long time and I've never heard of such a position. Any meaningful application is going to need a persistent connection to any TCP sockets opened. That's the whole purpose of TCP. The lifecycle of that connection is significant to the application, and the application should have a way of knowing if a NEW connection comes in, which right now is impossible with the way EthernetServer.cpp is coded.

I've been doing this a long, long time and I've never heard of such a position.

Then you haven't been doing this that long.

SurferTim:

I've been doing this a long, long time and I've never heard of such a position.

Then you haven't been doing this that long.

That is just a bizarre thing to say. I've been doing this for 20 years. Every client-server app I've been associated with has had statefull servers. Maybe you are referring to services, which is a different thing altogether. The Arduino could potentially be seen as a service presenter for something like checking the temperature of a probe at a specific point in time, but when you are actually writing a stateful server application, you need persistent TCP connections that you can manage in a reasonable manner. No management is not reasonable.

I guess it is bizarre, now that I think about it, newbie. I started working with multi-user computer systems in 1973. How old were you then?

I am quite capable of using persistent connections, but most client-server communication is exactly as PaulS stated. The client sends a request, and the server responds. End of story. If the client has anything else, it needs to connect-send-receive-disconnect.

SurferTim:
I guess it is bizarre, now that I think about it, newbie. I started working with multi-user computer systems in 1973. How old were you then?

I am quite capable of using persistent connections, but most client-server communication is exactly as PaulS stated. The client sends a request, and the server responds. End of story. If the client has anything else, it needs to connect-send-receive-disconnect.

So basically, you're telling me that I shouldn't attempt to write a stateful service for Arduino and that decades of applications that maintain state about connections are just designed wrong?

Oh no! That was not my point. My point was that most client-server communication is a one-time thing, repeated over and over. That is a result of the internet and web servers.

I use persistent connections, like for telnet, but to say that you have not heard of a http connection is also bizarre.

If you read a lot of my posts, you will find I am into experimentation. If I can help, I will.

SurferTim:
Oh no! That was not my point. My point was that most client-server communication is a one-time thing, repeated over and over. That is a result of the internet and web servers.

I use persistent connections, like for telnet, but to say that you have not heard of a http connection is also bizarre.

If you read a lot of my posts, you will find I am into experimentation. If I can help, I will.

Even with HTTP you have to have some context about your client, especially if you become I/O bound waiting for data. With the Arduino programming model you let the loop method fall out so that you can let the device process other jobs. This means that one call through loop gets you one byte of data from the client. If you don't have state about the client, then you lose context on where to send the data on the next loop iteration. All the HTTP Server examples for Arduino use busy loops to process everything at once and then send it all out at once. That model fails when you throw in asynchronous data access. But I digress.

My application I'm writing today is the equivalent of a telnet server. I have an end-point on ethernet and an endpoint on serial. Because of this I can only have one connection be presented from ethernet to serial at a time. Simply not responding to subsequent connection attempts isn't an option. Some feedback needs to be given so that the caller knows that the resource is available, but currently in use. As they are currently written, you cannot differentiate between the allowed connection and the disallowed connections incoming connections. I don't want to reinvent the wheel to accomplish this, but if I have to I will.

The connection is what maintains the state of the client in a http connection. You can become I/O bound on either end for a short period of time, and the connection is still maintained. Unless there is an abnormally long delay, the connection can be maintained for quite a while. I use 10 seconds without any packets to determine if the connection is broken before closing my end.

With an http connection, you have a request, which may or may not be unique to that client. I noticed the forum here has a PHPSESSID in the url. That maintains some context with the client.

The cookies also contain information that is unique to a client, which caused me some grief lately when the forum server got me confused with someone else, and banned me from the forum. But those are not necessary for a http connection, just an add-on.

There is a challenge with some protocols, like in your case, that require the server to determine there is a client without the client sending anything. Normally email servers are that way. As soon as you connect, they send a hello message. The Arduino library is not capable of that now.

I see around the forum that there are other users discussing this same topic, so maybe, since this is all open source, we can get something like that put into the library. Many functions have been added to the ethernet library since I have been here.

edit: Personally, I think a version of pylon's function code in reply#1 may be the next candidate for the ethernet library. To round out the whole package, we need something like that.

This is a version which doesn't always return the same connection if there are multiple open connections:

EthernetClient EthernetServer::available()
{
  accept();

  int rand = micros() % MAX_SOCK_NUM;

  for (int s = 0; s < MAX_SOCK_NUM; s++) {
    int sock = (s + rand) % MAX_SOCK_NUM;
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port &&
        (client.status() == SnSR::ESTABLISHED ||
         client.status() == SnSR::CLOSE_WAIT)) {
      if (client.available()) {
        return client;
      }
    }
  }

  return EthernetClient(MAX_SOCK_NUM);
}

EthernetClient EthernetServer::connected()
{
  accept();
  int rand = micros() % MAX_SOCK_NUM;

  for (int s = 0; s < MAX_SOCK_NUM; s++) {
    int sock = (s + rand) % MAX_SOCK_NUM;
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port &&
         (client.status() == SnSR::ESTABLISHED ||
         (client.status() == SnSR::CLOSE_WAIT && client.available()))) {
      return client;
    }
  }

  return EthernetClient(MAX_SOCK_NUM);
}

It's not completely random but it's better than using always the same order to look for the next socket to handle.

@pylon: A while back, someone suggested that, and I kinda blew it off. I may have been a bit premature on that response. Now that I look at the code, it might start with the same socket every call. Maybe I was just lucky that it serviced them in order.

I'm not sure the random thing is a good way to handle it tho. As I recall, the user suggested storing the current socket number in global memory, and increment that value every call. That way it doesn't start with the first socket all the time.

I like the idea of the connected function. With that, other server protocols can be used.

I would also like to see a way to check the status of any socket with a high level function call. Something like this:

int Ethernet.socketStatus(socketNumber);

SurferTim:
I would also like to see a way to check the status of any socket with a high level function call. Something like this:

int Ethernet.socketStatus(socketNumber);

That's already possible with this two liner:

EthernetClient c(socketNumber);
if (c.status == SnSR::ESTABLISHED) // just an example

That's not really high level but the method you suggest wouldn't be that either.

I like that status also. I'm going to play with that for a while.