Sharing socket between class instances

Feb 21, 2023 at 12:58pm
I have two classes:
* BasexClient is used to manage all data between the client and an instance of BasexSocket
* BasexSocket is used for communication with the socket.
Each BasexClient has one private member Socket.

The readSocket function that kbw (9475) wrote for me (see https://cplusplus.com/forum/beginner/285198/) uses this lamda-function to test if the socket is read for reading:
1
2
3
4
5
6
7
8
auto can_read = [](int s) -> bool {
  fd_set read_set;
  FD_ZERO(&read_set);
  FD_SET(s, &read_set);
  struct timeval timeout {};
  int rc = select(s + 1, &read_set, NULL, NULL, &timeout);
  return (rc == 1) && FD_ISSET(s, &read_set);
};

Since I am using a non-blocking connection, in the authentication procedure I had to introduce a wait() function between sending credentials and reading the result.
I transformed the lamda-function to this wait() function
1
2
3
4
5
6
7
8
9
10
11
12
13
bool wait(int s) {
 fd_set read_set;
 struct timeval timeout {};
 memset(&timeout, 0, sizeof(timeout));
 bool done{};
 while (!done ) {
   FD_ZERO(&read_set);
   FD_SET(s, &read_set);
   int rc = select(s + 1, &read_set, NULL, NULL, &timeout);
   done = (rc == 1) && FD_ISSET(s, &read_set);
  };
  return done;
};

The authentication procedure gives no problems.
1
2
3
4
bytes_sent = writeData(auth);
wait(Master_sfd);                 // Avoid race-conditions
sock_read_string.clear(); 
bytes_read = readSocket( sock_read_string);

One of the methods from BasexClient is the Command method:
1
2
3
4
5
void BasexClient::Command(const std::string command) {
  std:: string exec, result;
  std::string response;
  addVoid(command, exec).handShake(exec, result).splitResponse(result, response);
};

And this the handshake method:
1
2
3
4
5
6
7
BasexClient BasexClient::handShake(std::string Input, std::string &result) {
  int bytes_sent = Socket.writeData(Input);
  int sock = Socket.get_Socket();
  Socket.wait(sock);
  int bytes_read = Socket.readSocket( result);
  return *this;
}


Line 9 in the wait() function gives problems.
When used in BasexSocket::Authenticate, int rc = select(s + 1, &read_set, NULL, NULL, &timeout); rc has value 4.
When used in the handshake Socket.wait(sock), rc has value 0.
When skipping Socket.waith() line 6 in the lambda gives the same result.

The arguments to select() are identical in both calls.

Why does select() returns different values?
Last edited on Feb 21, 2023 at 12:59pm
Feb 21, 2023 at 1:21pm
From MSDN

The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred


SOCKET_ERROR is -1 - for which you are not testing!

So a return value of 0 means time limit expired and 4 means that 4 socket handles are ready. If a 0 is returned (ie time out), what are going to do?

Where are you setting a time limit - as it looks like this is 0?

As a 'kludge fix' perhaps L10:

 
done = (rc > 0) && FD_ISSET(s, &read_set);


but this might result in an infinite loop....
Last edited on Feb 21, 2023 at 1:23pm
Feb 21, 2023 at 1:37pm
Authentication (send credentials => read check) is followed immediately by handshake(). Why should the authentication() procedure not suffer from a timeout, contrary to the handshake()?
This all take place within miliseconds.
Feb 21, 2023 at 4:36pm
For wait(), I'd be considering something like (NOT tried):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool wait(int s) {
	static constexpr unsigned timout { 100 };
	static constexpr unsigned retrys { 5 };
	fd_set read_set;
	struct timeval timeout {.tv_usec = timout};

	FD_ZERO(&read_set);
	FD_SET(s, &read_set);

	bool done {};

	for (unsigned rt { retrys }; !done && rt; )
		if (const auto rc { select(s + 1, &read_set, NULL, NULL, &timeout) }; rc < 0) {
			std::cout << "Error in select\n";
			break;
		} else if (rc == 0) {
			std::cout << "Timeout\n";
			--rt;
		} else
			done = FD_ISSET(s, &read_set);

	return done;
}

Feb 21, 2023 at 9:44pm
I just got home and didn't have time to look at the proposed change yet.
In the meantime I've been thinking about another solution. I can transfer the handShake method from BasexClient to BasexSocket without too much trouble. The advantage of this would be that all read and write operations are worked out in BasexSocket. I do wonder however if in that case I can stick to the current set-up where I open a connection once and continue to use it until the client has finished processing all commands.

Question:
Which approach is preferable, the above approach with 1 connection or an approach where I make a new connection for every atomic action and close it again after completion? (I think this question is a little off topic).
Feb 24, 2023 at 1:57pm
Dbuging and stepping through my code learned me that indeed there was a time out. This timeout was caused by a \0x00 that I had forgotten to append to the input for the handshake.

Adding a \0x00 introduced a new error, but this thread is now solved.
Topic archived. No new replies allowed.