Let’s break this up into parts. First of, the network is based in IP, which is a protocol that assigns an address to each network node, and which allows you to send small amounts of data (usually up to 64kB, but typically only 1500B) from one node to another.
That by itself isn’t worth much yet, because we can’t make any checks that the data actually arrived, and that it arrived in the right order. If we want an abstract mechanism to transmit arbitrary amounts of data and ensure that they arrived, we need another protocol on top of the network that handles this “transmission”. And that’s the purpose of TCP.
However, in parallel to TCP, there’s another “transmission” protocol that doesn’t do any checking at all and has no reliability, UDP. UDP is just a thin wrapper around raw IP packets, which adds a little bit of meta data (like a port number).
UDP is still useful, though, since there are many situations in which the data integrity is already handed off to an even higher protocol, so there’s no need for a complex transmission protocol. This is for example used in virtual networking services, where another instance of TCP/IP is typically run over a UDP channel. (Making the channel use a reliable protocol like TCP can actually have disastrous consequences in that case due to resend cascades.)
So the term “TCP connection” refers to the application of the TCProtocol. The protocol is stateful, naturally, and typically proceeds in a SYN-ACK-data-FIN sequence, or SYN/RST in case of a rejected transmission; both peers maintain a status of the connection (handshake, established, closing, closed.) TCP also introduces the terms “server” and “client”, the server being the peer that
listen()s for an incoming connection.