class BasexSocket {
public:
BasexSocket (const std::string, const std::string, const std::string, const std::string);
~BasexSocket() { close( Master_sfd);}
int get_Socket() {return Master_sfd;};
int readSocket( std::string &sockResponseString);
int writeData(const std::string & input);
int writeData(const std::vector<std::byte> & input);
private:
int Master_sfd;
BasexSocket& CreateSocket (string, string);
BasexSocket& Authenticate (string, string);
} ;
The constructor from BasexClient calls the constructor from BasexSocket and stores the socket descriptor as a private field.
readSocket() and (the first) writeData() are used in the authenticating process. No problems here.
Both addVoid functions are used to build an input vector for handShake().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
BasexClient BasexClient::handShake(std::vector<std::byte> Input, std::vector<std::byte> &Result) {
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (Socket.get_Socket(), SOL_SOCKET, SO_ERROR, &error, &len);
if (retval != 0) {
/* there was a problem getting the error code */
fprintf(stderr, "error getting socket error code: %s\n", strerror(retval));
}
if (error != 0) {
/* socket has a non zero error status */
fprintf(stderr, "socket error: %s\n", strerror(error));
}
Socket.writeData(Input);
return *this;
}
Calling handshake() results in the following output. The last line comes from the line "fprintf(stderr, "ERROR: (errno = %d), %s ", errno, strerror(errno));" in Socket.writeData().
error getting socket error code: Unknown error -1
ERROR: (errno = 9), Bad file descriptor libBasexTest: Writing data failed
My question is how I can access/use the socket file descriptor that is created in BasexSocket?
BasexSocket& BasexSocket::CreateSocket (std::string host, std::string port) {
if (host.empty() || port.empty()) {
Master_sfd = -1; return *this;
}
struct addrinfo hints;
struct addrinfo *result = NULL, *rp;
// Initialize hints
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET; // Accept both AF_INET and AF_INET6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV; // Port must be specified as number
int rc;
rc = getaddrinfo( host.c_str(), port.c_str(), &hints, &result);
if (rc != 0) perror(gai_strerror(rc));
for (rp = result; rp != NULL; rp = rp->ai_next) { // result is a linked list of address structures.
Master_sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (Master_sfd == -1) continue;
if (connect(Master_sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; // Try to connect. Return the first successfull connect or abort
close(Master_sfd);
}
set_nonblock_flag( Master_sfd, 1);
if (rp == NULL) {
warnx("Can not connect to Basex server"); }
freeaddrinfo(result);
return *this;
}
After creating the socket, the socket is used without problems in the authenticating process.
EDIT:
1: I changed in BasexClient.h
BasexSocket Socket; into BasexSocket *Socket;
2: In BasexClient.cpp
- In the constructor I changed Socket(DBHOST, DBPORT, DBUSERNAME, DBPASSWORD) into Socket(new BasexSocket(DBHOST, DBPORT, DBUSERNAME, DBPASSWORD))
- All calls to BasexSocket's methods were changed from Socket.writeData() into Socket->writeData()
I have not yet implemented the readSocket part in BasexClient::handShake but writeData() executes without error and reports the correct number of bytes is sent.
Can you explain why introducing the pointer solves the problem?
I see that BasexSocket does not handle being copied correctly. If the BasexSocket object was accidentally copied you would end up with two BasexSocket objects with the same file descriptor. If one of them was destroyed it would close the file descriptor leaving the other object with a bad file descriptor.
I don't say this necessarily what happens, but to rule it out and prevent it from happening you might want disable copying by declaring the copy constructor and copy assignment operator as deleted.
After a second look through the code I noticed that addVoid and handShake returns a copy of the BasexClient object. This means that the BasexSocket object that the BasexClient object contains will also be copied leading to the problem I described above.
I am using Eclipse.
When using the default C++ managed build projects, I know where I can set the C++ dialect that has to be used.
But in these CMake projects, I haven't found where I can set this value, other than in CMakeLists.txt
I thought that these lines would give the same result (I tried both of them):
Thanks to all the help I finally managed to successfully retrieve information from the database for the first time.
Besides studying all the examples, I learned the most by just experimenting.
Just a further point. When passing std::string as a function param, you'd usually pass by const ref (const atd::string& ) [or by using std::move semantics but that is a little more advanced topic] rather than by value as now to avoid an unnecessary copy.
I learned the most by just experimenting.
Yes - but be careful. C++ is one of those languages where you have to learn the basics and underlying principles as to how 'things work'. Just because it seems 'to work' doesn't mean the code is right!
After I learned to program in Basic on a Commodore 64, I successively programmed in Pascal, Fortran and Clipper. Some colleagues moved to C about 30 years ago, and I heard from them about many programming errors associated with memory misuse. That was the reason for me to switch to the alternative Java.
Much later I learned to program in SWI-Prolog. That's really fun and challenging! That is also the reason for me to still learn C++.
After I have also programmed in R (see the RBaseX client), I eventually want to build a Prolog client for the BaseX database, but because SWI-prolog does not have enough low-level functionality for using a socket, I first have to develop a library that does offer that functionality. A full C++ client for the database is more or less an accidental spinoff.
All in all this will take a long time but as a pensioner I have enough time ;-)
Ben
PS. I'll add the references when passing a std::string
PS 2. I'm constantly checking the cplusplus.com, cppreference.com, and learncpp.com sites to get to grips with C++ as a development tool.