Howdy. I am in a networking class and we are creating our own “networking API” using the socket functions we have learned in class thus far.
For the assignment the professor provided the already complete chat and server programs and we were to fill in a blank .c file that had an associated header file that described the function calls in our little “networking API” that the chat and server programs used.
I am close to done but have ran into a problem. I was gonna go in and mess with my code, but all the other parts of my “networking API” work so I didn’t want to to screw with it when I am so close to done and have a little change cause other parts not to work. Plus I am not 100% sure that I do in fact know what my bug is.
My problem is with my function called recv_line that is supposed to mimic recv().
My function recv_line gets handed the file descriptor from the client for the connection that was setup with the server. I then go ahead and associate a stream with the file descriptor using fdopen. The bit of code looks like:
// Associate a stream with the sock_fd
if (NULL == (fdstream = fdopen(sock_fd, "r"))) {
return -1;
}
Now this is where I believe my bug to be but I am not 100% sure as I said. Firstly, the first time I use my recv_line function it works great. It does in fact recv_line the line as expected. But the problem arises the second time around.
I stepped through my function with GDB and the above bit of code does excute without problems. I then go on to peel off a character from my stream (using fgetc) and check if it is equal to the EOF char. If it is I return, else I will process it. The second time that I call recv_line it returns cuse it read the EOF char every time.
I’m assuming this is happening because the data sent to the client is in the original stream (the one created the first time around) and the second time I call fdopen there isn’t any data in the second stream I created? I’m not completely sure how fdopen works when I call it the second time around?
On I side note I my reading of my data this way as we are supposed to continue reading data until we read an EOF or the character sequence \r\n. I use getc and ungetc with the stream to provide a look ahead mechanism that will check for the EOF or the \r\n sequence.
So with all the being said, does anyone know what exactly is going on with my big of code and program? I Googled around for a method to check if a FD has an associated stream to it and wasn’t successfully in finding/seeing anything. I did some research to see if there was some fstat time function that would let me see associated steams and came up with nothing. Can I just dup the FD that is passed in to the function recv_line and then fdopen on the dupped FD and avoid all these problems?
Thanks for all the help. I am sorry if I wasn’t clear enough. I will do my best to clarify anything if you ask for me to do so. And sorry my question was so lengthy. =*(
Thanks again though. I really appreciate your help.
That is pretty much correct (with some minor corrections). The major duty of a stream object is to read the file descriptors data stream from the kernel to userland so you don’t need the overhead of a system call. But once the stream object reads the data stream, the kernel advances it’s ‘read pointer’ so the next time you try to read the data from the file descriptor, it picks up where you left off.
You can only call fdopen one time for a given descriptor. If you can restructure your code so that there is a place where you can easily call fdopen once, that is the way to go.
Once you call fdopen, you should no longer do anything with the file descriptor – not even close it (when you call fclose the underlying descriptor will be closed).
Based on Jonathon Leffler’s comment, I’m guessing the professor’s code is also going to close the descriptor. If that is true, you cannot use fdopen.
Some ideas:
Update based on comment
@Chris – let me explain the risk of using fdopen. Once you have used fdopen on a file descriptor, you must call fclose (or you risk leaking resources allocated for the FILE *) and you cannot call close on the descriptor (since fclose the descriptor for you can run into problems closing the descriptor multiple times). So, unless your code has already been delegated the responsibility of closing the descriptor (in which case you can then just call fclose), you can’t use fdopen.
There is no built in way to detect if fdopen has been called on a descriptor. You need to add logic yourself. You would need to keep a collection of FILE *’s returned by fdopen; if you already had a FILE * for a given descriptor, you would use it and otherwise you would call fdopen then.