File descriptors provide a primitive, low-level interface to input and output operations.
Streams provide a higher-level interface, layered on top of the primitive file descriptor facilities. Since streams are implemented in terms of file descriptors, you can extract the file descriptor from a stream and perform low-level operations directly on the file descriptor. You can also initially open a connection as a file descriptor and then make a stream associated with that file descriptor. In general, you should stick with using streams rather than file descriptors, unless there is some specific operation you want to do that can only be done on a file descriptor. If you are concerned about portability of your programs to systems other than GNU, you should also be aware that file descriptors are not as portable as streams.