The difference is one of API design.
The .NET Socket
class is an OOP API for Win32's Winsock, which itself is derived from BSD Sockets.
The Socket
API is built around its own functions for sending and receiving data, with functions like send
, recv
. On many platforms you can use the OS-provided filesystem API for reading and writing to sockets the same way you can read and write to local files.
In .NET, the Stream
class exists as an abstraction of any source or sink for binary data which can be read in a blocking or non-blocking (async
) manner regardless of where it came from (a file on a local disk, a file on a network share, a buffer in-memory, a TCP connection, a UDP connection, and so on). Read more about Stream
and its use as an abstraction here: https://learn.microsoft.com/en-us/dotnet/standard/io/
The point is that if you write a program or library that processes data - then rather than having to repeat your code over-and-over for different types of IO (files, in-memory buffers, TCP connections, etc) you only need to write your code once using Stream
and then magically your code can be used in many new places without much work.
But this comes with a downside of leaky-abstractions. We've since learned over the past 20-30 years of software-engineering that a single interface will not be perfect in every role - for example, a MemoryStream
is always non-blocking and doesn't need flushing - but a NetworkStream
(a Stream
API for Socket
) behaves very differently despite sharing the same API (remember: interfaces do not describe behavior!), such as how it buffers data internally (e.g. Nagle's algorithm). This is why .NET is now moving away from Stream
and towards the new Pipeline
API model.
So, in short:
- Network connections are always using a
Socket
internally.
- The
TcpClient
object adapts the Socket
API to the Stream
API (as NetworkStream
).
- So a
TcpClient
cannot exist without a Socket
.
- And a
NetworkStream
is simply an adapter for Socket
for the Stream
API.
- If your program does not need to abstract-away IO using the
Stream
API then you should only use the Socket
API and not use NetworkStream
or TcpClient
.
- If you do need to pass network data around using a
Stream
model, then use TcpClient
and NetworkStream
- but be-aware of how NetworkStream
behaves and you should always use non-blocking (async, aka "overlapped IO") to avoid bottlenecks and program freezes.