The short answer is: By using multithreading techniques.
The long answer is that events are really just a signal that is raised by other code that's continuously checking for a particular set of circumstances. For example a very simple portion of code responsible for raising the DataAvailable event might look like this:
While Socket Is Connected
If Data Is Available Raise Event DataAvailable
Loop
The .Net library has a Socket class which you can wrap with a class that you design to behave a bit more like the Winsock class that was available in VB6. Once you dig around the documentation for the Socket class and learn how to create custom events for a class it's not difficult to imagine how such a wrapper might be designed. The real hurdle for most developers coming from VB6 (and for me) is that you must learn a bit about multithreaded applications in order for it to work properly.
You can probably find such a wrapper by doing a bit of searching but I would highly encourage you to at least attempt writing your own. With single cored machines quickly becoming a thing of the past experience with multithreading techniques is going to become a requirement of any decent programmer.