Hello Mark.
Although TCP is a very simple protocol to use, it is very complex
underneath and it gets very tricky it you start changing default values
[1] [2], due to many implications you cannot control easily.
If you start changing TCP options moving it from defaults, you also
have to consider how this relates to other configurations in your
system AND, this is key, how all this will be handled by routers and
devices in the middle (which are more prepared for the usual case) not
only in your premises but also in hypothetical customer hardware you
don't know yet.
The problems relies on the fact that people using TCP are expecting it
to be really resistant to network failures so it does a lot of work for
you for free, recovering, retransmitting, translating, mitigating, etc,
without the application noticing it.
They don't care but people want it delivered, ordered, without no app
complications.
That's why network operators, middle routers or even the smallest DSL
nat router, do fancy things with TCP connections and its associated
state: because doing so they increase chances to recover the connection
without TCP peer noticing it, so they can keep on delivering traffic,
ordered and with as much as possible resource consumption (CGNAT aren't
free).
So if give a try to resolve keep alive problems by using just TCP's
support you will find it is hard: it will require from you a very deep
knowledge about TCP, how it relates to your hardware and scenario, but
also you will have to be prepared to face very strange and bizarre
problems (because there is a lot in the middle).
I would never recommend, but possibly you can afford this with a very
custom solution where all hardware and software is under control.
This is not the case for MQTT where heterogeneous scenario will be
usual.
So, your question "Why do we need another KeepAlive mechanism when we
have TCP's?" is assuming three false premises:
1) You have full control to change whatever you want at TCP level
without no consequences.
2) TCP works in a ideal case where you are point-to-point connected to
the other end so there is no theoretical possibility to have a traffic
loss, network connection problems, traffic congestion, attacks [3], etc.
3) That good amount of overbooked heterogeneous full-of-quirks hardware
working in the middle of TCP peers doing best effort does not exists.
So protocol designers have come to a simple conclusion:
1) It is far more easier to have a PINGREQ and PINGRESP at application
level (resolves the problem).
2) We can have full control of it at application level, with low
coupling with the-TCP-ecosystem (resolves implications).
3) Takes nothing to reason about it and how it works (low cost
solution).
4) It allows to check TCP, but also application level (more
comprehensive check).
5) AND allows to use the-TCP-ecosystem in a regular way (total bonus).
In essence, the point is not why do you need a KeepAlive mechanism at
MQTT level, but that you will end up building one after attempting to
fight the problem at TCP level (understanding TCP is not just a
protocol but a really big and old ecosystem).
Best Regards.
[1]
http://codearcana.com/posts/2015/08/28/tcp-keepalive-is-a-lie.html
[2]
https://stackoverflow.com/questions/24133668/tcp-keepalive-not-working
[3] Not mentioned, but this is something you will have to evaluate too.