Perfection isn't possible. But you can probably get pretty close basing your synchronization on ntp's time synchronization strategy.
Basically, you can designate a master, have the client send a packet, including send time, have the server copy that to a response packet, also recording it's receive time, and time of response. The client issues sync requests until it's satisfied that it's seeing reasonably consistent latency measures, and then adjusts its clock.
More or less.
And then it's just a matter of having one or the other take responsibility for setting start and stop times. And deciding how critical it is that the other app not miss the first beat. (Versus delaying start time to account for network congestion.) E.g., start 70bpm, effective at
time is in the past when the slave receives the message, just compute the rate and when the next beat must occur.