It's kinda like:
+-------------------------------------------------------+
| client network server |
+-----------------+ +--------------------|
| (connect) | ---- SYN ----> | |
| | <-- SYN,ACK -- | (accepted) |
| (connected) | ---- ACK ----> | |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
when client sends...
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
| | | |
| (send) | ---- data ---> | |
| | <---- ACK ---- | (data received) |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
when server sends...
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
| | | |
| | <--- data ---- | (send) |
| (data received) | ---- ACK ----> | |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
...and so on, til the connection is shut down or reset
SYN starts a connection; you'll usually only see it when the connection's being established. But all data being sent via TCP requires an ACK. Every byte sent must be accounted for, or it will be retransmitted (or the connection reset (closed), in severe cases).
Actual connections aren't usually exactly like the diagram above, though, for two reasons:
- ACKs can build up, so one ACK can acknowledge everything received up to that point. That means you can acknowledge two or more sends with one ACK.
- An ACK is simply a flag and field in a TCP header. Sending one requires at least a header's worth of bandwidth, plus whatever the lower layers tack on. But data segments already include all that...so if you're sending data, you can send an ACK at the same time for free.
Most TCP/IP stacks try to reduce the number of naked ACKs without unduly risking retransmission or a connection reset. So a conversation like this one is quite possible:
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
| | | |
| | <--- data ---- | (send) |
| (data received) | | |
| (send) | -- data,ACK -> | |
| | | (data received) |
| | <- data,ACK -- | (send) |
| (data received) | | |
| (wait a bit) | <--- data ---- | (send) |
| (data received) | | |
| (send) | -- data,ACK -> | |
| | | (data received) |
| (send) | ---- data ---> | (wait a bit) |
| | | (data received) |
| | <- data,ACK -- | (send) |
| (data received) | | |
| (wait a bit) | (dead air) | |
| | ---- ACK ----> | |
\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
As for UDP, there's no built-in concept of SYN and ACK -- UDP is by nature "unreliable", and not connection-oriented, so the concepts don't apply as much. Your acknowledgement will usually just be the server's response. But some application-layer protocols built on top of UDP will have some protocol-specific way of acknowledging data sent and received.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…