I’m trying to implement a TCP client in C which needs to work as follows:
- Ability to open a connection to a given server
- Ability to send arbitrary data to the server through the established connection, and
- Ability to receive arbitrary data from the server (think of it as responses to the ‘questions’ that my client sent to the server).
For example, the client should be able to open a connection to an arbitrary HTTP server, send the ‘HEAD’ message and print the response that arrives from the HTTP server.
(My goal is to create a generic ‘TCP client plugin’ for a specific software environment that I’m using for my daily work and which lacks networking abilities. I know the SDK of my environment quite well, but I don’t really have a deep experience in socket programming.)
Currently, I have 2 separate threads for sending and receiving data. The workflow of the receiver thread (which starts automatically as soon as the server address & port is set by the user) is the following one (here I only mention the main sequence of socket calls):
globalSocket = socket(); // Create socket and store it globally
bind(); // Bind the local port
connect(); // Connect to remote host & port
listen(); // Listen to the socket
while (isAlive) {
select(... &readfds ...); // Check for ready reader descriptors
accept(); // Accept the incoming connection
recv(); // Receive data from server
}
close(); // End the connection
The sender thread is quite simple, it uses the globalSocket created by the receiver thread to execute the send() command.
Now, here’s my problem: I can open connections to remote servers without any problem. I can also send any arbitrary data without any problem (I confirmed that the data that I send actually arrives to the server side without problems). However, I can’t get any data back from the server. After some tests, it seems that select would never return with a positive value.
I tried a lot of modifications in my code (like changing parameters to select, omitting listen etc.), I read Beej’s guide at least 10 times during this very day and tried every ad-hoc change that I just could imagine, but the behaviour is still the same. Therefore, before I start asking specific questions with specific code excerpts, I’d like to know whether my approach to this problem was correct or whether I’m having some serious conceptual problems here.
Thanks indeed for your answers,
Ádám
P.S. I couldn’t post this codepart in the comments as it is too long; here’s the code snippet that manages the select – accept – recv cycle:
while ( thread->isActive ) {
// Accept connection
timeVal.tv_sec = TIMEOUT_SEC;
timeVal.tv_usec = TIMEOUT_USEC;
FD_ZERO( & fileDescriptor );
FD_SET( socketDescriptor, & fileDescriptor ); // socketDescriptor is the global socket
result = select ( FD_SETSIZE, & fileDescriptor, NULL, NULL, & timeVal );
if ( result > 0 ) {
post ( "select" ); // This line is actually never reached
connectionDescriptor = accept ( socketDescriptor, ( struct sockaddr * ) & clientAddress, & clientLength ); // connectionDescriptor is the local socket created by accept
if ( connectionDescriptor < 0 ) { // Some error happened
outlet_int ( thread->parent->statusOutlet, errno );
} else {
// Receive data
data.clear ( ); // 'data' is an std::vector of chars that stores the incoming data and makes it accessible for the rest of the environment
size = thread->parent->bufferSize;
buffer = new unsigned char [ size ]; // this buffer is used for receiving the data from 'recv'
receivedBytes = 1;
while ( receivedBytes > 0 ) {
receivedBytes = recv ( connectionDescriptor, ( char * ) buffer, size, 0 );
if ( receivedBytes < 0 ) { // Socket error
outlet_int ( thread->parent->statusOutlet, errno );
}
data.insert ( data.end ( ), buffer, buffer + receivedBytes );
}
delete [ ] buffer;
#ifdef WIN_VERSION
closesocket ( connectionDescriptor );
#else
close ( connectionDescriptor );
#endif
// Output received data
... blah ... blah ... blah
}
}
}
So, there are several independent issues here, which are split between several answers and comments, so I’m going to summarize them.
The sender thread is OK. However, the connector/listener thread should look as follows:
Regarding the originally posted code excerpt, the
whilecycle that was being used to handlerecvis a conceptual error and should be removed, since it would block the thread – which would make theselectstatement unnecessary (since in that case,selectwould have no practical effect).It should also be pointed out that if
recvreturns 0, that means that the socket was closed by the remote peer, so in that case thewhile (isAlive)loop should be aborted even ifisAlivewas not set to false by the parent thread. However, an extraclosestatement is not needed inside the loop (which was also a conceptual error in the original code).