There are multiple options to this, but one popular one is to encode everything you do in a game as actions transmitted over the network and putting their "time of execution" slightly to the future. The core idea here is to give the action (such as player moving a tetris tile) some time to reach the other player's program before executing it from a queue or such.
The latency between clients can then be adjusted to fit the network speed. In case of an out of sync packet arriving, there are a few options depending on how your game is constructed. If it's something simple like tetris, you can probably just implement a resync that sends over the current gamestate in some neat format. Another option if the actions player can take are limited is to simply resync the delta since some unit in time. This requires you to keep some amount of history of the gamestate in the memory.
The solution can vary depending on how your game actually is like. Stuff like tetris blocks not being 100% in sync is not a big deal while victory conditions are. By this I mean that having a way to verify who wins first is more important, but other kind of resyncs can be done on the fly without affecting the player's experience vastly. In a game like Tetris (sorry for getting hung up on that), the player could technically be playing against a recording without ever knowing it (also an used technique in games like this, but not fit for all games that want to be more "real-time").
If you're using TCP sockets, missed packets will be fixed on their own so you're saved that trouble.
I think that in order to receive more specific information on the subject, you probably have to describe the context in a bit more detail.