The TCP 3-way handshake works like this:
Client ------SYN-----> Server Client <---ACK/SYN---- Server Client ------ACK-----> Server
Why not just this?
Client ------SYN-----> Server Client <-----ACK------ Server
Break down the handshake into what it is really doing.
In TCP, the two parties keep track of what they have sent by using a Sequence number. Effectively it ends up being a running byte count of everything that was sent. The receiving party can use the opposite speaker's sequence number to acknowledge what it has received.
But the sequence number doesn't start at 0. It starts at the ISN (Initial Sequence Number), which is a randomly chosen value. And since TCP is a bi-directional communication, both parties can "speak", and therefore both must randomly generate an ISN as their starting Sequence Number. Which in turn means, both parties need to notify the other party of their starting ISN.
So you end up with this sequence of events for a start of a TCP conversation between Alice and Bob:
Alice ---> Bob SYNchronize with my Initial Sequence Number of X Alice <--- Bob I received your syn, I ACKnowledge that I am ready for [X+1] Alice <--- Bob SYNchronize with my Initial Sequence Number of Y Alice ---> Bob I received your syn, I ACKnowledge that I am ready for [Y+1]
Notice, four events are occurring:
In actuality though, the middle two events (#2 and #3) happen in the same packet. What makes a packet a
ACK is simply a binary flag turned on or off inside each TCP header, so there is nothing preventing both of these flags from being enabled on the same packet. So the three-way handshake ends up being:
Bob <--- Alice SYN Bob ---> Alice SYN ACK Bob <--- Alice ACK
Notice the two instances of "SYN" and "ACK", one of each, in both directions.
So to come back to your question, why not just use a two-way handshake? The short answer is because a two way handshake would only allow one party to establish an ISN, and the other party to acknowledge it. Which means only one party can send data.
But TCP is a bi-directional communication protocol, which means either end ought to be able to send data reliably. Both parties need to establish an ISN, and both parties need to acknowledge the other's ISN.
So in effect, what you have is exactly your description of the two-way handshake, but in each direction. Hence, four events occurring. And again, the middle two flags happen in the same packet. As such three packets are involved in a full TCP connection initiation process.
The three-way handshake is necessary because both parties need to synchronize their segment sequence numbers used during their transmission. For this, each of them sends (in turn) a SYN segment with a sequence number set to a random value n, which then is acknowledged by the other party via a ACK segment with a sequence number set to n+1.
In order for the connection to work, each side needs to verify that it can send packets to the other side. The only way to be sure that you got a packet to the other side is by getting a packet from them that, by definition, would not have been sent unless the packet you sent got through. TCP essentially uses two kinds of messages for this: SYN (to request proof that this packet got through) and ACK (which only gets sent after a SYN gets through, to prove that the SYN got through). There's actually a third kind of message, but we'll get to that in a moment.
Before the connection starts, neither side really knows anything about the other. The client sends a SYN packet to the server, to request proof that its messages can get through. That doesn't tell either person anything, but it's the first step of the handshake.
If the SYN gets through, then the server knows that the client can send packets to it, because, well, it just happened. But that doesn't prove that the server can send packets back: clients can send SYNs for lots of reasons. So the server needs to send two messages back to the client: an ACK (to prove that the SYN got through) and a SYN (to request an ACK of its own). TCP combines these two messages into one -a SYN-ACK message, if you will- to reduce network traffic. This is the second step of the handshake.
Because a SYN-ACK is an ACK, the client now knows for sure that it can send packets to the server. And because a SYN-ACK is a SYN, it also knows that the server wants proof that this message got through. So it sends back an ACK: just a plain ACK this time, because it doesn't need proof anymore that its packets can get through. This is the final step of the handshake: the client now knows that packets can go both ways, and that the server is just about to figure this out (because it knows the ACK will go through).
Once that ACK gets through, now the server knows that it can send packets to the client. It also knows that the client knows this, so it can start sending data right away. The handshake is complete. We have a good channel.
Well, strictly speaking, we can't be certain we have a good channel. Just because this sequence of packets got through does not strictly guarantee that others will. We can't prove that without sending an infinite number of SYNs and ACKs, and then nothing else would ever get done, so that's not really a practical option. But in practice, three steps turns out to be good enough for most purposes.
Actually, a 3-way handshake isn't the only means of establishing a TCP connection. Simultaneous SYN exchange is also allowed: http://www.tcpipguide.com/free/t_TCPConnectionEstablishmentProcessTheThreeWayHandsh-4.htm
That could be seen as a sort of double 2-way handshake.
TCP connection is bidirectional. What this means is that it actually is a pair of one-way connections. The initiator sends SYN, the responder sends ACK: one simplex connection begins. "Then" the responder sends SYN, the initiator sends ACK: another simplex connection begins. Two simplex connections form one duplex TCP session, agree? So logically there are four steps involved; but because SYN and ACK flags are different "fields" of TCP header, they can be set simultaneously - the second and the third steps (of the four) are combined, so technically there are three packet exchanges. Each simplex (half-)connection uses 2-way exchange, as you proposed.
If Server and Client want to create a connection, they need confirm four things:
Client need to confirm he can receive packet from Server
Client need to confirm a thing: Server can receive packet from Client
Client ------SYN-----> Server, rule 1 is confirmed.
Client <---ACK/SYN---- Server, rule 2 and 3 is confirmed.
So, there need a third packet to confirm rule 4.
It is not necessary at all. It is obvious that a short message should only require one packet to the server which includes the start + message, and one packet back acknowledging it.
The previous answers just describe the system without discussing the need for random sequence numbers etc. in the first place. The original question was about the design of TCP itself -- obviously if you use the TCP protocol then you need three messages because that is the protocol. But why was TCP designed that way in the first place?
I believe the original idea was that there was no distinction between clients and servers. Both knew the other's ports in a bidirectional manner, and either could start the conversation. And that required Syns etc.
But this is not, of course, how it is used today. The server listens on a well known port and does and "accept", the client port number is ephemeral. I do not even think it is possible for a server waiting on an "accept" to send a request to another on the same client port number in normal operating systems.
(Note that this is about bidirectional initiation of the connection, which is never done today. That is quite different from sending bidirectional messages down a connection once established.)
To work around the TCP inefficiency, we use protocols like HTTP 1.1 which can reuse the same connection for multiple requests, and thus avoid the TCP handshake which was not necessary in the first place.
But Http 1.1 is relatively new. And SSL/TLS needed a way to reuse session from the beginning due to the cost of the PKI algorithms. So that protocol includes its own session reuse mechanism which runs on top of Http 1.1 which runs on top of TCP.
Such is the way with software. Fudges on kludges which when combined, produce an acceptable result.
After reading answer of Eddie (accepted as correct), there are still question why 1st host can not assign both ISN's with random numbers and 2nd just accept it. Real reason of using 3-way handshake is to avoid half-connections. Half connection scenario in 2-way handshake:
1) Client ---SYN--> Server
2) Client changes his mind and doesn't want to connect anymore
3) Client <-X-ACK-- Server //ACK was lost
Server doesn't see resent SYN, so he thinks that client got his ACK and connection is established. As a result Server has connection that will never be closed