Long late overdue response. But I hope this will help a bit.
cherrypy.engine is basically like the core loop of CherryPy.
The goal of the engine is to provide a solid management of the process and all the components running as part of it.
It is designed around a simple pub/sub approach.
When an instance of the engine starts, it sends messages to various channels you can subscribe to.
Anything that wants to run as part of the engine lifecycle must abide by this interface of channels and messages.
The cherrypy.server is obviously the main component that you want to run in a CherryPy application.
But the server still has to hook itself to these messages to be executed and part of the lifecycle of the engine.
You can see this happening here:
That subscribe() call ensures the server will be running as part of the engine (the main loop if you will).
So engine and server have different purposes but the server makes use of the engine lifecycle to be a good citizen during the process's life.
I hope this helps a bit?
By the way, I'm the author of the book, I appreciate you reading it. It's 15 years old, I wouldn't count it as a good resource any longer :p
I wrote a lot of the documentation too years ago but it may still be lacking. Sorry for that :(