Então, outro dia de depuração e, finalmente, tenho uma explicação.
1) Os SAEAs não estavam disparando o evento concluído porque não conseguiram enviar mais. Isso é revelado pelo Wireshark devido ao esvaziamento da janela TCP. (TCP Zerowindow)
2) A janela do TCP estava esvaziando porque a camada de rede estava passando por um evento na pilha que demorou muito para concluir, ou seja, não há produtor/consumidor entre a camada de rede e a interface do usuário. Assim, o Network OP teria que esperar pelo desenho da tela antes de enviar o ACK.
3) O evento que demorou demais foi um empate em um manipulador de eventos na GUI. O equipamento de teste era uma janela do console (que resumia as mensagens recebidas), por isso não causou um problema com carga muito mais alta. É normal não redesenhar a tela em cada mensagem, mas isso estava acontecendo porque o projeto ainda não acabou. A taxa de redefinição teria sido consertada posteriormente.
4) A solução de curto prazo é simplesmente garantir que não haja GUIs segurando o show. Uma solução mais robusta pode ser criar um produtor/consumidor na camada de rede.