Sharing socket between class instances

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
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
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.
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;
}

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).
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.