A socket is a broader concept than a buffer. A socket has two buffers and some other information associated with it.
In the context of sockets programming, a socket is your app's interface to one TCP connection (or UDP flow). Your app doesn't read/write data from/to the network interface card (NIC) directly, it goes through the kernel's network stack. Socket buffers are the short queues of packets the kernel holds on behalf of your app, as it's shuffling data between the NIC and your app's memory space.
A send buffer or write buffer is the queue of packets your app has handed off to the network stack to send, and a receive buffer or read buffer is the queue of packets the kernel's network stack has received on your app's behalf, that it's waiting for your app to read (that is, it's stored in kernel space, waiting for your app/process to ask for it to be copied into your app's memory).
On the sender side, for best performance, your app must call
write() often enough, with enough data, to make sure the kernel's send buffer never runs out of data to send. We call this "keeping the pipe full". But to minimize latency, your app shouldn't load up the kernel's send buffer with too much data.
On the receiver side, for best performance, your app must call
read() often enough to try to keep the kernel's receive buffer empty. Otherwise, if the receive buffer doesn't have enough free space, the network stack will signal to the sender that its receive buffer is filling up, causing the sender to slow down the rate at which it's sending data.