Jump to content
  • Advertisement
Sign in to follow this  
Matias Goldberg

TCP's 3-way handshake... why not 2-way?

This topic is 2045 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everybody.
I'm writting a simple reliability layer on top of UDP. Not that I want to reinvent the wheel, just most of the data I'll be sending can be sent unreliable but there are bits of data every now and then that need to be sent reliably; and mixing tcp & udp gets even more troublesome.
For establishing a connection, I'm well aware of the 3-way handshake that TCP uses "SYN; SYN-ACK; ACK"
However, what I don't get, is why the third ACK is needed. Yes, I know it's the ultimate proof that the connection has been acknoledged by both parties; but I don't believe it's absolutely necessary.
This question has already been raised in StackOverflow, however the answers there don't satisfy me:
Let's see a communication without the 3rd ack:

Case: Client didn't get SYN-ACK
In this case, Server thinks the connection is established, Client think it's not. Just send another SYN from client until the SYN-ACK is received
Server may start sending data to client because it doesn't know the SYN-ACK got through. Client won't ACK that data because his connection isn't yet established, so server will continue to keep sending data over & over again until timeout. When the Client successfully gets the syn-ack; it will ack that data, hopefully before the timeout. If timed out, server-side it will just look like one connection timed out, and another came in. It's important that server doesn't use the SYNs from client as proof of heartbeat.
Typical view on this: The server needs to allocate resources (for tcp). More SYNs received -> more resources. However I send a random ID generated client-side with the SYN. That way the server identifies the SYN & IP with associated resources using that ID (and deals with client reconnecting and starting a new session as they'll change their ID; or with another client getting the same IP)
ACKs from normal messages always send the ID along the sequence number. So that if client reconnects (or new client got the same IP & port old client had) the server won't think an ack received from an old session is acknowledging packets sent from the current session.


A connection could be hijacked only if a machine gets the same IP address (and port), happens to use the same ID the other client has been using, and all happens before timeout (or there is a man in the middle that can see all data, spoof the IP, and still read the answers from server because he can see all data going to that spoofed IP).

But it's not like TCP is foolproof to hijacking either. Granted, this method is a lot easier to hijack because the ID, IP & port is repeated in every ack. Furthermore I'm interested in preventing "accidental" hijacking, not directed attacks.
The only disadvantage I can see: Potentially much higher bandwidth consumption (because the server may start sending data while client won't acknoledge it), while TCP needs to account for congestion control (which I don't care).
In TCP, everything is silent until SYN-SYN-ACK-ACK has been performed.


Bigger ACKs as a disadvantage could also be mentioned, but TCP overhead is already much bigger than UDP, and again, most of the data I'll be sending is unreliable (no need for ack), while every now and then I send some reliable data (needs guaranteed delivery, guaranteed to arrive in order)



Am I missing something? Why is the third ACK needed?


Share this post

Link to post
Share on other sites

The ACK is accompanied by sequence numbers.


The RFC is pretty clear on the handshake:


1) A --> B SYN my sequence number is X
2) A <-- B ACK your sequence number is X
3) A <-- B SYN my sequence number is Y
4) A --> B ACK your sequence number is Y


Steps 2 and 3 are combined in a single packet.


The thing about it is that step 4 is also what is used to move the TCP window.  By including that step you are using the regular protocol in the initialization process.  It is basically just zeroing out the transmission windows.




You aren't blocked from sending data along with step 4.


You could send data with your first ACK if you wanted to, essentially saying "I received packets up to Y, here is some data".

Share this post

Link to post
Share on other sites

Ahhhhhh thanks a lot. That explains everything.

In my simplified layer both A & B start their sequence at 0. I knew tcp randomized the start, but I didn't realize both A and B (not just B) need to send their start.


Yeah, that answers my question. I knew I was missing something obvious.


Thank you very much!

Share this post

Link to post
Share on other sites

In my simplified layer both A & B start their sequence at 0.

By doing that, you run a greater risk that replays may confuse the protocol state, and the risk that an attacker may be able to inject his/her own packets because the state is "known." That may or may not actually matter for your case, but it did matter for the TCP designers back in a day where packets would be duplicated and a copy delivered 10 seconds late.

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!