This is my first post; I hope it is in the correct category.
I was reading the official Rust language book and (finally!) reached the last chapter, which implements a simple HTTP server using Rust.
I noticed that the following is written about TCP and HTTP in the first section:
The two main protocols involved in web servers are Hypertext Transfer Protocol(HTTP) and Transmission Control Protocol(TCP) . Both protocols are request-response protocols, meaning a client initiates requests and a server listens to the requests and provides a response to the client. The contents of those requests and responses are defined by the protocols.
Unlike HTTP, I think it is wrong to describe TCP as a request-response protocol. Maybe it is more appropriate in this context to describe it as a client-server protocol.
You're absolutely 100% right. Describing TCP itself as "Request-Response" is plain wrong.
Technically, establishing a TCP connection exposes properties of a request-response cycle: a connection request (SYN) and accepting the connection (ACK).
However, once the connection is established, a TCP connection is a bidirectional data stream. Generally, this is not understood as a request-response protocol.
If you argue this is request and response, then it would be the same as saying adding two numbers is request and response: a request to the CPU to add two numbers and a response with the sum.
It's somewhat sad that such an easy fix to clarify the paragraph has not been accepted, for whatever reason. To me, this looks completely arbitrary.
The “service” that TCP provides to the application layer is the reliable, ordered, and error-checked delivery of a stream of octets. Each send() transmits the next n outbound bytes (octets), and each recv() retrieves the next n inbound bytes (octets). But you certainly must not expect that a single recv() retrieves a full “message” – because TCP knows nothing about application-level messages. There is absolutely no guarantee that each send() will be matched by a corresponding recv() of the same size. Instead, you'll have to call recv() in a loop, and concatenate the received bytes, until you have the “full” message. How you determine whether a message is complete depends entirely on the application-level protocol. Finally, even though TCP allows for bidirectional communication – and most application-level protocols, like HTTP, actually do this – TCP can be used for unidirectional communication; there is no requirement in TCP that data must be transmitted in both directions. In this sense, TCP is not a “request-response” protocol.
TCP implements its service by sending individual IP packets over the wire. Specifically, on the sender's side, TCP will split the outgoing byte stream (from the application layer) into separate IP packets. Similarly, on the receiver's side, TCP will reassemble the incoming byte stream from the received IP packets and pass it up to the application. Each TCP/IP packet contains a sequence number, so that packets can be resembled in correct order, even if they were received out-of-order. Also, each received TCP/IP packet is confirmed by sending an “acknowledgement” packet back to the sender. If the “acknowledgement” is not received by the sender in due time, then it will re-send the previous packet(s). In this sense, i.e., each sent TCP packet is acknowledged, TCP is a “request-response” protocol.
(as opposed to UDP, where each packet is sent according to the “fire and forget” principle)
TCP sequence numbers are byte positions, not packet numbers or IDs. Acknowledging data by a receiver is typically delayed. There are some clever algorithm at work in TCP to get high throughput with sliding windows and delayed ACKs.
There is no 1:1 relationship between receiving packets and sending acknowledgements.
(If data is transmitted in both directions, acknowledgements are “piggybacked” on data packets that go in the opposite direction, so there often is no need for separate “pure ACK” packets. Also, a single acknowledgement can acknowledge data from more than one received packet. Still, every piece of application data that is transmitted via TCP needs to be acknowledged eventually)
In my opinion, it should be looked at from the perspective of the data exchange happening on the protocol. TCP does include internal mechanisms to provide its services, but - as you mentioned - the service it provides is an arbitrary bidirectional byte stream.
HTTP exchange units, on the other hand, are either a request or a response.
An analogy to this is a conversation between two people versus a greeting between them. In a conversation, not everything mentioned warrants a response or is even a request (for example, if I am giving someone some instructions). On the other hand, a greeting usually involves some kind of a "request" and the other person gives back a "response" to it for the greeting to be "completed".