(Just before this session started, I spotted Avri Adleman in a suit. Avri had done many C and assembler sessions at prior SHAREs, and he was one of the last people that I'd have ever thought to see in a suit. "Yeah", he grumbled, "I can't wait to get rid of this. But I'm on the NomCom, and we have a show to put on." He shrugged with a resigned grin. NomCom? I hadn't heard the term before, but a chance glance at his badge revealed that he was on the nominating committee. Oh. Glad it's him and not me!)
Of course, the sockets inventors had to reinvent the language, and they came up with terms to learn. "Sockets" are endpoints of a connection. An "association" is a connection between two processes on the same or different machines. A "protocol suite" is similar to what other people call a "protocol stack". I'll spare you the other examples.
I was surprised to learn that sockets don't require TCP/IP, and that use of the sockets API is independent of the underlying transport protocol. But most sockets implementations are for TCP/IP anyway, and the two have becomes more or less synonymous with each other.
When you initialize a connection (um... an association), you are obligated to specify the underlying protocol. This is why you see the "AFINET" constant in the socket program samples. It stands for "Addressing Family - InterNET", and tells the socket implementation that you expect it to use TCP/IP as the transport protocol.
It is possible for two different processes to bind to the same TCP/IP port, though if you're doing so it's probably a design error. A program can bind to a port, and then issue a Unix fork() - causing an identical twin of the process to be made, complete with already open files and sockets! You can also issue a setsockopt() with a "reuse" parameter that allows you to bind to busy ports. Sounds dangerous to me, but Unix has always had a reputation for allowing you to shoot yourself in the foot if you like.
The second hour of this presentation went into gory line by line detail of the C socket API, using source code to implement an REXEC client and server. The details are too many to list here.
Because a socket looks like a plain old Unix file after the connection is established, a program uses read() and write() calls to receive and send data. But classic Unix I/O has no knowledge of records; data is read or written as a byte stream, with no structure to it at all. So when a program at one end of a socket connection calls write() to send 100 bytes of data, the program at the other end might have to call read() three times to receive 25 bytes, 70 bytes, then 20 bytes of the same data. There is no way to know how many bytes will be returned from a read(), so the receiving program has to do some assembly. This is a common opportunity for errors.