Its useful in that you can then add extra information that the base exception class does not hold.
So you can create a NetworkException class and that would contain information about the socket error code and/or the destination connection address or whatever you liked. Callers can catch this and present better information for diagnosis of the error.
However you now start to get into an analysis issue - why bother writing all that info and a new exception class when chances are, the caller who catches your exception will just write all that custom info into a string and dump it into log file or display it as 'detailed information' to the user - you might as well write it to the base classes string member and have done with it, save everyone a load of hassle.
But, if you did have a custom exception class, the client can catch only those that they care about and let others continue up the call stack for someone else to handle - eg the caller might catch a NetworkException when the user clicks connect, but let an OutOfMemoryException continue and .. well, usually crash the client.
So its a bit of one of those areas where you have to think hard about the design, and often no answer you come up with is the "right" one, any are just as good. (for example, I have a tool written in C#, and there are lots and lots of Microsoft exception classes that I might catch... although I do catch a few special exception classes for certain circumstances, I still tend to catch all of them and print out the base class message anyway (or worse - the innerexception message, don't go there). This is a admin tool so writing out every exception message is what I need, and every Microsoft exception contains a message with the details I want to log so I really don't care about the exception class!
so its up to you, though I would advise against creating a huge inheritance hierarchy of every possible error, that way lies a lot of maintenance and documentation trouble.