Given this is just an academic exercise, assuming that there is infinite bandwidth, zero latency (pipes), and logic should all run on one of the two instances:
Given a standard game loop of "wait for game tick, update logic, render state to screen", the loop would differ between client and server side:
update logic = Capture local input (i.e rotate left, drop piece, etc.) and send to server. Receive state (location of pieces, scores, etc.) from remote server. If state is not received yet, use state from last execution. If receive more than 1 state, drop all but the last.
render state = draw current state to screen.
update logic = Capture local input, and remote input. If remote input is empty/not arrived, assume no action from remote. Update game state for both players. Send entire game state to the remote.
render state = draw current state to screen -- same as in client side