15.04.2013 Views

Prof V Vargas, IST Jogo do Galo (Resolução) Page 1 of 39 Projecto ...

Prof V Vargas, IST Jogo do Galo (Resolução) Page 1 of 39 Projecto ...

Prof V Vargas, IST Jogo do Galo (Resolução) Page 1 of 39 Projecto ...

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 1 <strong>of</strong> <strong>39</strong><br />

<strong>Projecto</strong> de S<strong>of</strong>tware "Cliente/Servi<strong>do</strong>r"<br />

<strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>)<br />

{<strong>Jogo</strong>Do<strong>Galo</strong>-r.<strong>do</strong>c}<br />

Nota introdutória: este <strong>do</strong>cumento tem fundamentalmente <strong>do</strong>is objectivos, a saber:<br />

1. apresentar uma Especificação de um Protocolo, concretamente o <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (que foi o enuncia<strong>do</strong><br />

<strong>do</strong> <strong>Projecto</strong> Cliente-Servi<strong>do</strong>r em 2004-2005). Na prática, ela resume-se ao Anexo-A (Para bom entende<strong>do</strong>r:<br />

submeti<strong>do</strong> o enuncia<strong>do</strong> de um <strong>Projecto</strong>, aguarda-se que os estudantes entreguem, como Especificação, algo<br />

análogo às 9 páginas por que se desenrola o Anexo-A abaixo). As páginas que o precedem destinam-se tãosomente<br />

a apresentar a reflexão que conduziu àquela Especificação – começan<strong>do</strong> por introduzir conceitos a<br />

propósito: Cliente e Servi<strong>do</strong>r, Primitivas, Diagramas Temporais, PDUs, sockets TCP/UDP, Arquitectura de<br />

Comunicações, Diagramas de Esta<strong>do</strong>, Eventos e Predica<strong>do</strong>s, Tabelas de Esta<strong>do</strong>, etc..<br />

2. apresentar uma meto<strong>do</strong>logia para a Codificação desse Protocolo. Na prática, ela ocupa o Anexo-B.<br />

I - Preliminares: Servi<strong>do</strong>r e Clientes co-residentes<br />

Considere-se, para simplificar, uma Associação Recreativa com um único computa<strong>do</strong>r (Unix) e uma Sala de<br />

<strong>Jogo</strong> com uns quantos Terminais.<br />

Quan<strong>do</strong> um sócio quer jogar (o <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>), senta-se a um Terminal, procede ao login - e continua invocan<strong>do</strong><br />

o programa Joga<strong>do</strong>r e digitan<strong>do</strong> Start. Depois, só tem que aguardar que surja um outro sócio... e que também ele<br />

corra o programa Joga<strong>do</strong>r (num outro Terminal, é claro), todavia digitan<strong>do</strong> agora Join.<br />

Para que as "coisas" funcionem, bastará então que esteja corren<strong>do</strong> um "media<strong>do</strong>r" entre os sócios, que aceite as<br />

jogadas (Play) nos tecla<strong>do</strong>s e lhes apresente nos ecrãs a evolução <strong>do</strong> tabuleiro...<br />

Para um conhece<strong>do</strong>r de Unix, será natural (fig. JdGrIa):<br />

- associar a cada sócio um processo Cliente - cria<strong>do</strong><br />

quan<strong>do</strong> ele invocou o programa Joga<strong>do</strong>r;<br />

- e conceber um processo Servi<strong>do</strong>r que materialize o<br />

"Media<strong>do</strong>r" supracita<strong>do</strong> - e que deverá ter si<strong>do</strong> cria<strong>do</strong><br />

antes mesmo de os sócios correrem o Joga<strong>do</strong>r:<br />

provavelmente, logo no power-on <strong>do</strong> computa<strong>do</strong>r.<br />

Trata-se de um cenário simples: um só computa<strong>do</strong>r,<br />

com um Servi<strong>do</strong>r e um conjunto de Clientes residin<strong>do</strong> nesse mesmo computa<strong>do</strong>r.<br />

II -Preliminares: Primitivas de Comunicação<br />

Servi<strong>do</strong>r e Clientes necessitam comunicar entre si. Em particular, aquan<strong>do</strong> de uma jogada (Play),<br />

- o Cliente necessita comunicar ao Servi<strong>do</strong>r o trio GId/Line/Column digita<strong>do</strong> pelo sócio;<br />

- e o Servi<strong>do</strong>r necessita comunicar a ambos os Clientes a subsequente posição <strong>do</strong> tabuleiro.<br />

Pressuposto que tais informações (GId/Line/Column e Board) serão comunicadas através de mensagens, é<br />

pertinente a pergunta: Como se processará a comunicação entre<br />

Servi<strong>do</strong>r e Clientes?<br />

Uma solução será fazê-lo por memória partilhada (fig. JdG-rIb):<br />

entre cada Cliente e o Servi<strong>do</strong>r, o Sistema Operativo reserva um par<br />

de memórias FIFO específicas:<br />

- uma, dita de saída, onde o Cliente escreve mensagens para o<br />

Servi<strong>do</strong>r ler,<br />

- e outra, dita de entrada, onde o Servi<strong>do</strong>r escreve mensagens para o Cliente ler.<br />

À "composição de uma mensagem e sua escrita", e à "sua posterior leitura e interpretação", é tradicional, no<br />

mun<strong>do</strong> <strong>do</strong>s protocolos de comunicação, atribuir a designação de primitivas.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 2 <strong>of</strong> <strong>39</strong><br />

Concretamente, quan<strong>do</strong> um sócio digitar "Play GId Line Column" (fig. JdG-rIc).<br />

- o respectivo Cliente Joga<strong>do</strong>r deverá escrever na sua memória de saída aquele trio GId/Line/Column (isto<br />

designan<strong>do</strong>-se de primitiva PLAY.req)<br />

- e, subsequentemente, o Servi<strong>do</strong>r deverá proceder à sua leitura (isto designan<strong>do</strong>-se de primitiva PLAY.ind)<br />

Os sufixos ".req" e ".ind" acrescenta<strong>do</strong>s a PLAY são as mnemónicas de request e indication.<br />

Posteriormente, o Servi<strong>do</strong>r valida a jogada, e reporta-o ao Cliente:<br />

- o Servi<strong>do</strong>r deverá escrever na memória de entrada <strong>do</strong> Cliente a sua conclusão, Ok ou Sorry (isto designan<strong>do</strong>se<br />

de primitiva PLAY.rsp)<br />

- e, subsequentemente, o Cliente deverá proceder à sua leitura (isto designan<strong>do</strong>-se de primitiva PLAY.cnf).<br />

Os sufixos ".rsp" e ".cnf" são as mnemónicas de response e confirmation.<br />

Se a jogada for válida, o Servi<strong>do</strong>r prossegue actualizan<strong>do</strong> o tabuleiro e divulgan<strong>do</strong>-o aos Clientes em causa:<br />

- o Servi<strong>do</strong>r deverá escrever na memória de entrada de cada Cliente <strong>do</strong> par de sócios em causa a posição <strong>do</strong><br />

tabuleiro (isto designan<strong>do</strong>-se de primitiva BOARD.req)<br />

- e, subsequentemente, esse Cliente deverá proceder à sua leitura (isto designan<strong>do</strong>-se de primitiva BOARD.ind)<br />

- após o que apresentará no respectivo ecrã a posição <strong>do</strong> tabuleiro.<br />

Repare-se: qualquer um, Cliente ou Servi<strong>do</strong>r, pode desencadear uma primitiva.<br />

- o Cliente recorre a PLAY.req para escrever uma Mensagem (GId/Line/Column) na sua memória de saída -<br />

que o Servi<strong>do</strong>r lê recorren<strong>do</strong> a PLAY.ind; subsequentemente, o Servi<strong>do</strong>r recorre a PLAY.rsp para escrever outra<br />

Mensagem (Ok ou Sorry) na memória de entrada <strong>do</strong> Cliente - que este lê recorren<strong>do</strong> a PLAY.cnf;<br />

- o Servi<strong>do</strong>r recorre a BOARD.req para escrever outra Mensagem (posição <strong>do</strong> tabuleiro) na memória de<br />

entrada <strong>do</strong> Cliente - que este lê recorren<strong>do</strong> a BOARD.ind.<br />

A primitiva PLAY diz-se confirmada - no senti<strong>do</strong> de que ao ".req" inicial se sucede uma - e uma só! - ".cnf"<br />

local. Com BOARD, isso não acontece: trata-se de uma primitiva não-confirmada.<br />

Para caracterizar cabalmente uma primitiva é então necessário especificar:<br />

- qual das partes a emite,<br />

- se é, ou não, confirmada;<br />

- quais os seus parâmetros (no caso, GId/Line/Column, Resulta<strong>do</strong> (Ok ou Sorry) e Board).<br />

Adiante, serão consideradas outras primitivas mais.<br />

III - Preliminares: Diagramas Temporais<br />

A troca de mensagens entre os intervenientes (Clientes e Memórias de Comunicação) pode esquematizar-se por<br />

um assim denomina<strong>do</strong> Diagrama Temporal. Nele, os intervenientes são representa<strong>do</strong>s por riscas verticais; o tempo<br />

corre verticalmente, de cima para baixo; pelo que a troca de Mensagens vem a ser representada por "setas" oblíquas<br />

(cf. JdG-rIIa, que figura a troca de Mensagens aquan<strong>do</strong> de uma Jogada, Play, bem sucedida).


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 3 <strong>of</strong> <strong>39</strong><br />

Três reparos:<br />

I. Uma "seta" estabelece o movimento físico de bits, de uma área da RAM para outra; é delimitada por uma<br />

ponta e uma cauda. Marcam os instantes em que respectivamente o último e o primeiro bits de uma Mensagem<br />

foram escritos (ou li<strong>do</strong>s) na Memória partilhada.<br />

II. O Diagrama não tem escala:<br />

Por um la<strong>do</strong>, desconhece-se a velocidade <strong>do</strong> processa<strong>do</strong>r; pelo que não se sabe a inclinação das setas.<br />

Por outro la<strong>do</strong>, não se conhece a carga <strong>do</strong> Computa<strong>do</strong>r no momento; pelo que se ignora o tempo de espera<br />

até se começar a escrever (ou ler) uma Mensagem. Por ex., desconhece-se quanto tempo decorre entre a escrita<br />

na Memória <strong>do</strong> resulta<strong>do</strong> Ok e a subsequente escrita da posição <strong>do</strong> tabuleiro.<br />

III. Os Diagramas dão conta somente <strong>do</strong> movimento físico das mensagens - <strong>do</strong> espaço de endereços dum<br />

processo para o <strong>do</strong> outro através das memórias partilhadas. São omissos relativamente aos sinais de controlo<br />

troca<strong>do</strong>s, cf. JdG-rIIc, que esquematiza uma possível sucessão de acontecimentos ao invocar-se PLAY.req:<br />

1. O Cliente preenche a mensagem num buffer seu - após o que invoca a SystemCall de escrita na memória<br />

de Saída;<br />

2. O conteú<strong>do</strong> <strong>do</strong> buffer é transferi<strong>do</strong> para essa memória;<br />

3. Com uma SystemCall, o Cliente solicita ao Sistema<br />

Operativo: alerte o Servi<strong>do</strong>r, que tem algo naquela memória;<br />

4. O Sistema Operativo notifica o Servi<strong>do</strong>r: há algo para ler…<br />

5. O Servi<strong>do</strong>r invoca a SystemCall de leitura daquela memória;<br />

6. O conteú<strong>do</strong> da memória é transferi<strong>do</strong> para um buffer <strong>do</strong><br />

Servi<strong>do</strong>r; ele pode agora proceder ao "parse" desse buffer: concretamente, extrai dele o trio GId/Line/Column.<br />

7. Com uma SystemCall, solicita ao Sistema Operativo: alerte o Cliente, que a memória de Saída já foi lida (e<br />

por conseguinte está disponível para nova escrita);<br />

8. O Sistema Operativo notifica o Cliente: a memória de Saída está livre…<br />

PLAY.req e PLAY.ind envolvem os passos 1-3 e 5-7. Os sinais 4 e 8 destinam-se a evitar underflow e overflow:<br />

PLAY.ind é desencadea<strong>do</strong> no sinal 4; e PLAY.req pode consumar-se somente após o sinal 8.<br />

IV - Preliminares: Mensagens e PDUs<br />

A troca de Mensagens entre Cliente e Servi<strong>do</strong>r inicia-se sempre pela invocação de primitivas .req ou .rsp; ela<br />

volve-se na escrita de sequência de bits nas memórias partilhadas entre ambos. A essas sequências de bits, é<br />

tradicional, no mun<strong>do</strong> <strong>do</strong>s protocolos de comunicação, designar de PDUS (Protocol Data Units). Deverão exibir<br />

uma estrutura (variável) que, no mínimo, inclua os valores <strong>do</strong>s parâmetros pertinentes àquelas primitivas.<br />

Por exemplo,<br />

- a sequência PLAY.req → PLAY.ind deverá envolver uma PDU, seja cPlay-Pdu, veiculan<strong>do</strong> (sob a forma de<br />

três int) o trio GId/Line/Column digita<strong>do</strong> no Cliente;<br />

- a sequência PLAY.rsp → PLAY.cnf deverá envolver uma PDU, seja sPlay-Pdu, veiculan<strong>do</strong> (sob a forma de<br />

um Boolean[+String]) a decisão <strong>do</strong> Servi<strong>do</strong>r (Ok ou Sorry[+Reason]);<br />

- e a sequência BOARD.req → BOARD.ind deverá envolver uma PDU, seja sBoard-Pdu, veiculan<strong>do</strong> (sob a<br />

forma de uma lista de ints e/ou chars) a posição <strong>do</strong> tabuleiro.<br />

Os prefixos "c" (em cPlay-Pdu) e "s" (em sPlay-Pdu e sBoard-Pdu) especificam qual das partes (Cliente ou<br />

Servi<strong>do</strong>r) preencheu a PDU escrita na memória partilhada.<br />

A estrutura de uma PDU fica especificada quan<strong>do</strong> se sabe:<br />

- quais os campos que a compõem,<br />

- se são, ou não, opcionais - e qual a respectiva ordem no seio da PDU;<br />

- por cada campo, qual o seu tamanho, tipo e valores aceitáveis: int, long, char, string, boolean, etc.<br />

Para a generalidade das Aplicações, há um campo imprescindível na estrutura da PDU: é o assim denomina<strong>do</strong><br />

Código <strong>do</strong> Tipo de PDU. Para o compreender, e ten<strong>do</strong> por pano de fun<strong>do</strong> o Diagrama Temporal acima,<br />

considerem-se, por ex., PLAY.cnf e BOARD.ind. No caso em apreço, pode afirmar-se que, para o Cliente que as<br />

recebe, são, ambas, previsíveis: acontecem quan<strong>do</strong>, ten<strong>do</strong> invoca<strong>do</strong> PLAY.req, ele recolhe o que o Servi<strong>do</strong>r


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 4 <strong>of</strong> <strong>39</strong><br />

escreveu na sua memória de entrada. PLAY.cnf é expectável - pois PLAY é confirmada. Quanto a BOARD.ind,<br />

também ela será expectável - se PLAY.cnf certificar que GId/Line/Column era váli<strong>do</strong>!<br />

A ter em conta, porém, a eventualidade <strong>do</strong> Servi<strong>do</strong>r abortar o <strong>Jogo</strong>, essa previsibilidade já não é tão certa: é<br />

possível que, em vez de PLAY.cnf ou BOARD.ind, o Cliente venha a receber um, seja, ABORT.ind. É então<br />

natural a pergunta: como é que o Cliente "sabe" que se trata de PLAY.cnf, BOARD.ind ou outra "coisa" qualquer?<br />

A resposta intui-se: sem ajudas, não o sabe… Convém, por conseguinte, um artifício, aliás trivial: que, na<br />

estrutura da PDU, se reserve um campo (conjunto de bits) para codificar o tipo de PDU em jogo.<br />

Isso significa que o Cliente sabe qual a primitiva a invocar somente após proceder à leitura na memória de<br />

entrada <strong>do</strong> Código <strong>do</strong> Tipo de PDU (Enuncia<strong>do</strong> análogo vale para o Servi<strong>do</strong>r). Aliás, será também esse código que<br />

lhe dirá como interpretar o resto <strong>do</strong> conteú<strong>do</strong> daquela memória: quais os campos e respectivos valores em presença.<br />

A tabela adiante apresenta, para cada Sequência de primitivas, o Tipo de PDU envolvida, um seu possível Código,<br />

e os demais campos que a constituem.<br />

Sequência de Tipo de Código <strong>do</strong><br />

Campos<br />

Primitivas<br />

PDU Tipo de PDU<br />

da PDU<br />

PLAY.req, PLAY.ind cPlay-Pdu 8 GId, Line, Column<br />

PLAY.rsp, PLAY.cnf sPlay-Pdu 9 Ok ou Sorry+Reason<br />

BOARD.req, BOARD.ind sBoard 10 GId, Board<br />

ABORT.req, ABORT.ind sAbort 6 GId, Reason<br />

Por ex., quan<strong>do</strong> um joga<strong>do</strong>r digita Play 4 2 3, o respectivo Cliente deverá escrever na sua memória de saída a<br />

PDU (e admitin<strong>do</strong> que os campos em jogo são to<strong>do</strong>s eles inteiros codifica<strong>do</strong>s em 8-bits):<br />

Código <strong>do</strong> Tipo de PDU (cPlay-Pdu):8 GId:4 Line:2 Column:3<br />

PDU: 0000 1000 00000100 0000 0010 0000 0011<br />

Esse coman<strong>do</strong> pode ser váli<strong>do</strong>: será o caso de o jogo "4" estar decorren<strong>do</strong>, e de ainda estar livre a posição<br />

linha=2,coluna=3; nesse caso, o Servi<strong>do</strong>r deverá escrever na memória de entrada daquele Cliente a PDU:<br />

Código <strong>do</strong> Tipo de PDU (sPlay-Pdu):9 GId:4 Result:Ok<br />

PDU: 0000 1001 00000100 0000 0000<br />

Mas pode suceder que o coman<strong>do</strong> não seja váli<strong>do</strong> - porque, por ex., a posição referida está já preenchida; então,<br />

a PDU devolvida pelo Servi<strong>do</strong>r será:<br />

Código <strong>do</strong> Tipo de PDU (sPlay-Pdu):9 GId:4 Result:Sorry Reason<br />

PDU: 0000 1001 00000100 0000 0001 Posição já preenchida<br />

Para bom entende<strong>do</strong>r: o Result é codifica<strong>do</strong> como 0 (Ok) ou 1 (Sorry); e, neste último caso, a PDU transporta<br />

uma mensagem explicativa <strong>do</strong> erro detecta<strong>do</strong>. Em alternativa, Sorry poderia veicular o código desse erro - bastan<strong>do</strong><br />

ao Cliente deter a lista <strong>do</strong>s códigos de erro possíveis - e correspondentes mensagens na língua materna <strong>do</strong> joga<strong>do</strong>r...<br />

Para o caso de o Result ser Ok, o Servi<strong>do</strong>r deve enviar o Tabuleiro; uma solução possível poderá ser:<br />

Código <strong>do</strong> Tipo de PDU (sBoard-Pdu):6 GId:4 Result:Tabuleiro<br />

PDU: 0000 0110 00000100 LXOLXOLLL<br />

(em que, por ex., LXO significa que as posições da linha são, respectivamente, "Livre", "X" e "O").<br />

A concluir a abordagem a este cenário (Servi<strong>do</strong>r e Clientes co-residentes), apresenta-se o esquema da<br />

Arquitectura <strong>do</strong> Sistema de Comunicações (fig. JdG-rIVb):<br />

Ambos os interlocutores apresentam uma mesma Arquitectura:<br />

1) um Elemento de Utiliza<strong>do</strong>r, que:<br />

- no Cliente, procede à leitura no tecla<strong>do</strong> e escrita no ecrã de cada terminal;


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 5 <strong>of</strong> <strong>39</strong><br />

- no Servi<strong>do</strong>r, procede à gestão <strong>do</strong> jogo, à escrita e leitura de ficheiros em disco, etc..<br />

- em ambos os casos, invoca as primitivas de Comunicação referidas.<br />

2) um Módulo de Comunicações, que executa as Primitivas referidas: procede ao empacotamento e<br />

desempacotamento de PDUs e à sua escrita e leitura nas Memórias partilhadas. Cabe-lhe, em particular, a<br />

invocação das chamadas ao Sistema Operativo relacionadas com o acesso a Memórias Partilhadas, com a<br />

Sincronização nesse acesso (para que, por ex., o Servi<strong>do</strong>r faça uma leitura só após uma escrita pelo Cliente), etc.…<br />

V - Servi<strong>do</strong>r e Clientes em distintos computa<strong>do</strong>res<br />

É altura de afrontar o cenário mais geral: ao menos um sócio - ou pela sua provecta idade ou por estar <strong>do</strong>ente -<br />

não pode deslocar-se fisicamente à Associação Recreativa. Fica em casa… mas, felizmente, dispõe de um<br />

"Magalhães" (per<strong>do</strong>e-se a publicidade ao made in Portugal) - e a tentação não se faz demorar muito: que impede<br />

usá-lo para embarcar num <strong>Jogo</strong> remoto através da Internet com o computa<strong>do</strong>r naquela Associação? O cenário é<br />

agora outro: ao menos um Cliente reside num computa<strong>do</strong>r distinto daquele onde corre o Servi<strong>do</strong>r (fig. JdG-rIIb).<br />

Como responder agora à pergunta: Como se processará a comunicação entre o Servi<strong>do</strong>r e esse Cliente?<br />

Memória partilhada já não pode ser solução: o que existe agora entre ambos é uma porção da Internet, isto é, um<br />

intrinca<strong>do</strong> maior ou menor de linhas e nós, com as suas memórias, processa<strong>do</strong>res, modems, procedimentos, etc..<br />

Pelo que a escrita e leitura de uma Mensagem em uma memória partilhada há-de agora ser substituída pelo<br />

"envio de uma Mensagem para a Internet e posterior recepção dessa Mensagem". Fica apenas por saber como<br />

levar a cabo esse tal envio e essa tal recepção…<br />

Ora acontece que os computa<strong>do</strong>res já hoje vêem equipa<strong>do</strong>s de s<strong>of</strong>tware vocaciona<strong>do</strong> para este envio/recepção:<br />

trata-se, concretamente <strong>do</strong> assim designa<strong>do</strong> s<strong>of</strong>tware TCP/UDP (e IP). Agora, já não se fala em memória partilhada<br />

- mas nuns objectos especiais ditos de sockets: em lugar de se escrever e ler numa memória partilhada, escreve-se e<br />

lê-se em um socket (fig. JdG-rIIIa).


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 6 <strong>of</strong> <strong>39</strong><br />

Pressuposto que Cliente e Servi<strong>do</strong>r dispõem, cada um, <strong>do</strong>s sockets necessários à comunicação entre ambos,<br />

intui-se o que acontece quan<strong>do</strong> um joga<strong>do</strong>r digita Play; grosso mo<strong>do</strong>,<br />

1. o Módulo de Comunicações <strong>do</strong> Cliente constrói a cPlay-Pdu, e escreve-a no seu socket;<br />

2. o s<strong>of</strong>tware TCP/UDP local encarrega-se de a enviar para a Internet;<br />

3. a Internet encaminha essa cPlay-Pdu até ao Servi<strong>do</strong>r;<br />

4. o s<strong>of</strong>tware TCP/UDP <strong>do</strong> Servi<strong>do</strong>r recolhe-a da Internet e apresenta-a no seu socket;<br />

5. o Módulo de Comunicações <strong>do</strong> Servi<strong>do</strong>r lê no socket a cPlay-Pdu e interpreta-a…<br />

VI - Arquitectura de Comunicações <strong>do</strong> "<strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>"<br />

A substituição da escrita/leitura em Memória partilhada pela escrita/leitura em sockets impõe uma pergunta:<br />

pretende-se preservar a Arquitectura esquematizada acima (Elemento Utiliza<strong>do</strong>r + Módulo de Comunicações), pois<br />

sócios haverá que continuarão a ir jogar nos terminais da Associação Recreativa… mas, para contemplar sócios<br />

aceden<strong>do</strong> remotamente, que adaptações lhe deverão ser feitas? A resposta exige uma revisão às funcionalidades<br />

<strong>of</strong>erecidas pelo s<strong>of</strong>tware TCP/UDP:<br />

Do ponto de vista <strong>do</strong> envio e recepção de da<strong>do</strong>s, TCP e UDP remetem para <strong>do</strong>is tipos de serviços, a que estão<br />

associa<strong>do</strong>s duas famílias de Chamadas ao Sistema:<br />

- em UDP, o envio e recepção de da<strong>do</strong>s logra-se com a invocação de sendto e recvfrom. Ambos operam sobre<br />

Blocos de octetos: com um sendto, envia-se o Bloco - e, para o receber na sua totalidade, basta um recvfrom. O<br />

serviço não é, porém, lá muito robusto: após sendto, o Bloco pode perder-se e não chegar ao destino…<br />

- em TCP, o envio e recepção de da<strong>do</strong>s logra-se com a invocação de write e read. Ambos operam sobre<br />

Sequências de octetos: com um ou mais write, envia-se uma Sequência - e, para a receber na sua totalidade, podem<br />

ser necessários um ou mais read, num número que até pode não ser<br />

exactamente igual ao número de write. O serviço é, porém, muito mais<br />

robusto que UDP: após o write, é muitíssimo menor a probabilidade de a<br />

Sequência não chegar ao seu destino - e muitíssimo maior a probabilidade<br />

de os octetos serem recebi<strong>do</strong>s pela mesmíssima ordem em que foram<br />

envia<strong>do</strong>s. …<br />

Podem agora deduzir-se as adaptações a fazer-se (fig. JdG-rIIIb):<br />

1. PLAY e BOARD, concebidas no cenário "Servi<strong>do</strong>r e Clientes coresidentes",<br />

acedem às PDUs de forma atómica: quan<strong>do</strong> o Cliente invoca<br />

PLAY.req, a consequência é a construção de uma cPlay-Pdu - em uma<br />

Memória partilhada que deve ter a dimensão suficiente (em número de<br />

octetos) para a conter na sua totalidade. Então, quan<strong>do</strong> o Servi<strong>do</strong>r invoca<br />

PLAY.ind, fá-lo assumin<strong>do</strong> que vai ler e interpretar toda essa PDU.<br />

2. No ambiente sockets, esta assumpção não pode ser feita:<br />

- se se usar sockets-UDP, o sendto da cPlay-Pdu não garante que o<br />

Servi<strong>do</strong>r a venha a ler: pode perder-se …<br />

- e, se se usar sockets-TCP, o write da cPlay-Pdu não garante que com um read o Servi<strong>do</strong>r a venha a ler na<br />

sua totalidade…<br />

Isso sugere, para o "<strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>", a arquitectura de comunicações apresentada em JdG-rIVa (no Anexo-A.1).<br />

Parêntesis: O Nível "Transparência "<br />

Tal Arquitectura inclui um assim denomina<strong>do</strong> nível "Transparência". A propósito, abra-se um parêntesis para<br />

tranquilizar o leitor. Considere, por ex., PLAY.req - que deve levar ao envio de uma cPlay-Pdu, de N=4 octetos. A<br />

delimitação resume-se ao seguinte:<br />

- proceder ao write de um certo número L de octetos (que neste Documento se assume ser 4) que veicule o<br />

tamanho (no caso, 4) daquela PDU;<br />

- e só então executar o write da própria cPlay-Pdu.<br />

A Internet acaba por vir a transportar um conjunto - designe-se de PDUNT - de, no total, L+N octetos:


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 7 <strong>of</strong> <strong>39</strong><br />

PDU-length ocupan<strong>do</strong> L=4 octetos, com o tamanho, N, da PDUMC<br />

PDUMC ocupan<strong>do</strong> N octetos<br />

É possível olhar para este conjunto da seguinte maneira: designe-se de PDUMC uma PDU genérica produzida<br />

pelo Módulo de Comunicações; tu<strong>do</strong> se passa como se o Nível Transparência a houvera encapsula<strong>do</strong> em uma<br />

outra, dita de PDUNT. Em alternativa aos <strong>do</strong>is write, o nível Transparência pode então prefaciar a cPlay-Pdu com<br />

um campo PDU-length de L octetos, e proceder a um único write de to<strong>do</strong> o conjunto.<br />

Então, o nível Transparência <strong>do</strong> Servi<strong>do</strong>r, quan<strong>do</strong> lhe chegar essa PDUNT, deverá passar por duas etapas:<br />

- primeiramente, lê L octetos, para saber qual o tamanho, N (no caso, 4), da PDUMC que chegou;<br />

- após o que, com um ou mais read, irá len<strong>do</strong> os octetos seguintes, até perfazer N octetos. No caso em apreço, só<br />

então será a hora de se executar PLAY.ind - passan<strong>do</strong> duma só vez to<strong>do</strong>s os octetos que compõem a cPlay-Pdu.<br />

Repare-se: ambos, Cliente e Servi<strong>do</strong>r, registam o mesmo número (três) de Níveis de Protocolo de<br />

Comunicações. Entretanto,<br />

- visto de mais perto, o nível TCP/UDP acaba por (dever) ser considera<strong>do</strong> como uma Pilha de Níveis de<br />

Protocolos de Comunicações (Físico, DataLink, Network e Transporte);<br />

- pode eliminar-se o nível Transparência - pelo expediente de incluir na estrutura das PDUs um campo<br />

"Tamanho da PDU" e, consequentemente, adaptan<strong>do</strong> o Módulo de Comunicações;<br />

- e nada impede que o Desenha<strong>do</strong>r <strong>do</strong> Sistema decida repartir o Módulo de Comunicações por (Sub-)Níveis de<br />

protocolos (por ex., para fazer criptografia, compactar da<strong>do</strong>s, re-sincronizar, etc.)<br />

VII - Primitivas de acesso aos Níveis de Protocolo<br />

VII.1: As Primitivas de acesso ao Nível TCP/UDP não deverão constituir novidade: write e read, etc.:<br />

Primitiva invocada por: Parâmetros<br />

connect Cliente Target (HostName + Port)<br />

accept Servi<strong>do</strong>r & Source (HostName + Port)<br />

write (e sendto)<br />

read (e recvfrom)<br />

close<br />

Cliente, Servi<strong>do</strong>r Socket, Buffer, Buffer-length, Flags (e Target)<br />

Socket, Buffer, & Buffer-length, Flags (e & Source)<br />

Socket<br />

select (puramente local) Cliente, Servi<strong>do</strong>r Domain, Type, Protocol<br />

VII.2: Quanto às Primitivas de acesso ao Nível Transparência, bastarão, no que toca à transferência de PDUs,<br />

as seguintes (fig. JdG-rIVc):<br />

Primitiva Parâmetros<br />

TCP.req (e UDP.req) Socket, Buffer, Buffer-length (e Target)<br />

TCP.ind (e UDP.ind) Socket, Buffer, & Buffer-length (e & Source)<br />

em que a discriminação TCP versus UDP selecciona qual o mo<strong>do</strong> como a comunicação se deve fazer:<br />

respectivamente por write+read ou sendto+recvfrom.<br />

VII.3: Já quanto às Primitivas de acesso ao Módulo de<br />

Comunicações, isso encontra-se em aberto. Na continuação <strong>do</strong> que se já<br />

discutiu acima, uma solução é começar por associar a cada coman<strong>do</strong><br />

(Start, Join, Play, List e Abort) uma ou mais Primitivas específicas:<br />

A Start, associa-se a primitiva START; ela é desencadeada pelo<br />

Cliente, e é confirmada: após o Utiliza<strong>do</strong>r digitar Start, ele deve receber<br />

uma resposta - que, em caso de sucesso, provém <strong>do</strong> Servi<strong>do</strong>r.<br />

De maneira análoga, a Join, Play e List associam-se respectivamente as primitivas JOIN, PLAY e L<strong>IST</strong>. Todas<br />

elas são desencadeadas pelo Cliente e, com excepção da última, são confirmadas.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 8 <strong>of</strong> <strong>39</strong><br />

Em caso de sucesso de Play, o Servi<strong>do</strong>r deve comunicar a subsequente "posição <strong>do</strong> Tabuleiro" a ambos os<br />

joga<strong>do</strong>res. Isso convida a conceber uma outra Primitiva, BOARD; ela é desencadeada pelo Servi<strong>do</strong>r (após<br />

responder a Play) e não é confirmada.<br />

Quanto a List, o Servi<strong>do</strong>r deve comunicar a Listagem <strong>do</strong>s <strong>Jogo</strong>s: GIds e respectivos joga<strong>do</strong>res. Isso convida a<br />

conceber outra Primitiva, GAMEL<strong>IST</strong>; é desencadeada pelo Servi<strong>do</strong>r e não é confirmada.<br />

Quanto ao Abort, existem três cenários:<br />

1. um joga<strong>do</strong>r digita Abort;<br />

2. o Servi<strong>do</strong>r acaba com um <strong>Jogo</strong> aborta<strong>do</strong> por algum fulano que nem sequer precisa estar a jogar:<br />

3. o Servi<strong>do</strong>r aborta um <strong>Jogo</strong> porque o perío<strong>do</strong> máximo entre coman<strong>do</strong>s expirou.<br />

Isso convida a conceber uma primitiva, ABORT, que pode ser desencadeada por qualquer uma das partes; e que,<br />

se for desencadeada por um Cliente e não for bem sucedida, deverá ser replicada por uma primitiva ERROR.<br />

Estas conclusões encontram-se sumariadas no Anexo-A.2 - num quadro que exibe, para cada Coman<strong>do</strong>, as<br />

primitivas associadas, quem as desencadeou e os respectivos parâmetros.<br />

Ressalve-se, nesse quadro, o parâmetro Gf. Surge para responder à interrogação: como "emparelhar" com um<br />

Start a posterior réplica (Ok ou Sorry) que o Servi<strong>do</strong>r irá devolver?<br />

- é que, por um la<strong>do</strong>, o GId é atribuí<strong>do</strong> pelo Servi<strong>do</strong>r: aquan<strong>do</strong> de START.req, ele não é ainda conheci<strong>do</strong>;<br />

- de outra parte, aceita-se que o utiliza<strong>do</strong>r possa digitar "de rajada" vários Start - mesmo sem antes ter recebi<strong>do</strong><br />

a réplica ao primeiro deles.<br />

Gf é escolhi<strong>do</strong> pelo Cliente, envia<strong>do</strong> na cStart-Pdu e depois "ecoa<strong>do</strong>" pelo Servi<strong>do</strong>r na subsequente réplica,<br />

sStart-Pdu. Repare-se, entretanto, que se poderá dispensar Gf: bastan<strong>do</strong> para tal que o Servi<strong>do</strong>r responda a cStart-<br />

Pdus pela mesmíssima ordem em que ele as for receben<strong>do</strong>…<br />

VIII - Diagramas Temporais das Primitivas<br />

A etapa seguinte é desenhar diagramas temporais para as primitivas concebidas. Eis alguns:<br />

VIII .T - Nível Transparência<br />

Apresentam-se <strong>do</strong>is Diagramas esquematizan<strong>do</strong> a sucessão de eventos desencadeada por uma TCP.req invocada<br />

pelo Módulo de Comunicações:<br />

JdG-rVa: O Nível Transparência adiciona o PDU-length e invoca write; o s<strong>of</strong>tware TCP envia o correspondente<br />

Segmento para a Internet; à sua chegada ao receptor remoto, o Nível Transparência invoca read; o PDU-length é<br />

removi<strong>do</strong>, e sinaliza<strong>do</strong> o Módulo de Comunicações - poden<strong>do</strong> então emitir-se TCP.ind.<br />

O Diagrama para UDP.req e UDP.ind é análogo - a diferença consistin<strong>do</strong> em que, agora, não é adiciona<strong>do</strong> nem<br />

removi<strong>do</strong> nenhum PDU-length, e o par write/read vem a ser substituí<strong>do</strong> por sendto/recvfrom.<br />

JdG-rVb: O cenário versa a eventualidade de o s<strong>of</strong>tware TCP/UDP achar que os da<strong>do</strong>s que acompanham o<br />

write são demasia<strong>do</strong> extensos para caberem num Segmento só: acabam por ser envia<strong>do</strong>s vários Segmentos para a<br />

Internet. No interlocutor remoto, isso dará azo a vários read… mas a presença <strong>do</strong> PDU-length conduz a que seja<br />

emitida uma única TCP.ind: esta só é invocada quan<strong>do</strong> a PDU já foi aí totalmente recebida…


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 9 <strong>of</strong> <strong>39</strong><br />

VIII .C - Módulo de Comunicações<br />

Um primeiro cenário respeita a Start, cf. JdG-rVIa. Considera-se que já existe uma conexão-TCP estabelecida<br />

entre o Cliente e o Servi<strong>do</strong>r; aquele executa START.req; esta é "ecoada" no Servi<strong>do</strong>r como START.ind. Ocorre<br />

sucesso: o Servi<strong>do</strong>r responde com START.rsp(Ok), que é "ecoada" no Cliente como START.cnf(Ok).<br />

Seguin<strong>do</strong> a evolução pari passu: um joga<strong>do</strong>r digita Start; o Elemento de Utiliza<strong>do</strong>r lê o tecla<strong>do</strong>, e invoca<br />

START.req; o Módulo de Comunicações constrói a cStart-Pdu, e invoca TCP.req; o Nível Transparência faz o<br />

que lhe compete… No Servi<strong>do</strong>r, o Módulo de Comunicações invoca TCP.ind, após o que lê a cStart-Pdu, e<br />

passa os respectivos valores ao Elemento de Utiliza<strong>do</strong>r. Este procede à actualização das suas estruturas de<br />

Da<strong>do</strong>s e devolve Start.rsp; o resto intui-se - e está contempla<strong>do</strong> no Diagrama…<br />

Nota: Se, eventualmente, já se esgotou o número de <strong>Jogo</strong>s simultâneos, então o Servi<strong>do</strong>r responde com<br />

START.rsp(Sorry), que é "ecoada" no Cliente como START.cnf(Sorry).<br />

No que segue, e por comodidade,<br />

1. TCP.req/TCP.ind e UDP.req/UDP.ind ficam implícitas: apenas se referem as cPlay-Pdu, sPlay-Pdu,<br />

etc.. As coisas são figuradas como se fosse o próprio Módulo de Comunicações a acrescentar/remover o<br />

PDU-length - e ele mesmo a invocar as primitivas write/read de acesso ao TCP.<br />

2. Ficam também implícitas as primitivas START.req, START.ind, etc. - no senti<strong>do</strong> de que se apresenta a<br />

evolução <strong>do</strong> conjunto Elemento-de-Utiliza<strong>do</strong>r+Módulo-de-Comunicações (e não cada um em separa<strong>do</strong>).<br />

Com isso, perde-se uma certa coerência: a referência ao TCP é feita em termos de primitivas (write,<br />

read, etc.) - que não em termos das correspondentes PDUs (segmentos); já a referência ao Módulo de<br />

Comunicações é feita em termos das suas PDUs - que não em termos das correspondentes primitivas.<br />

3. Omite-se o sufixo -Pdu nos nomes das PDUs, tanto nos Diagramas como nas Tabelas dele derivadas.<br />

Para exemplificar, em JdG-rVIf (em Anexo) apresenta-se o Diagrama Temporal correspondente ao já<br />

figura<strong>do</strong> em JdG-rVIa, mas agora refeito ten<strong>do</strong> implícitas todas as primitivas excepto write e read.<br />

No Anexo-A.3, apresentam-se outros Diagramas Temporais concernin<strong>do</strong> o <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>. Deverá ser trivial para<br />

o leitor congeminar outros cenários, nomeadamente:<br />

- Quanto o Abort é invoca<strong>do</strong> pelo Sócio, ainda não existe a necessária conexão-TCP com o Servi<strong>do</strong>r;<br />

- O Cliente de um (ou mais) <strong>Jogo</strong>s em curso s<strong>of</strong>re um crash; o Servi<strong>do</strong>r detecta-o e reporta-o aos pertinentes<br />

Clientes;<br />

- Ambos os joga<strong>do</strong>res invocam Abort…<br />

- Um <strong>do</strong>s joga<strong>do</strong>res invoca Start, outro invoca Join - e, precisamente nesse momento, o primeiro invoca<br />

Abort (fenómeno dito de colisão de Join e Abort).


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 10 <strong>of</strong> <strong>39</strong><br />

Nível Transparência, Diagrama <strong>do</strong> Receptor:<br />

IX - Preliminares: Diagramas de Esta<strong>do</strong>s<br />

A propósito <strong>do</strong> nível Transparência, apresentou-se o cenário JdG-rVb. Quiçá o leitor tenha repara<strong>do</strong>: a acção<br />

empreendida pelo receptor de read não é sempre a mesma: invoca TCP.ind somente ao terceiro read (fig. JdGrVc).<br />

Recorde-se:<br />

1. O Módulo de Comunicações compõe uma PDUMC, seja, por ex., a cPlay-Pdu 01 04 02 03(16), de N=4 bytes - e<br />

invoca TCP.req;<br />

2. o Nível Transparência prefacia-a com o PDU-length - onde inscreve o valor N -, resultan<strong>do</strong> a PDUNT 00 00<br />

00 04 01 04 02 03(16) e invoca write;<br />

3. o receptor começa por invocar read - para ler o tal PDU-length - fican<strong>do</strong> assim a conhecer o número de bytes<br />

da PDUMC;<br />

4. invoca então, um segun<strong>do</strong> read - para ler esses bytes - mas porventura nem to<strong>do</strong>s terão chega<strong>do</strong> já da Internet;<br />

terá então que esperar que o s<strong>of</strong>tware TCP lhe anuncie a chegada de mais bytes;<br />

5. quan<strong>do</strong> isso acontecer, invoca um terceiro read - com o que acaba de ler os bytes que faltavam… É então<br />

altura - e só então - de passar a PDUMC ao Módulo de Comunicações, através de TCP.ind.<br />

O receptor precisa, portanto, em cada momento, de<br />

memorizar o que já aconteceu (Enuncia<strong>do</strong> análogo vale<br />

para o transmissor). Isto remete para aquilo a que é<br />

tradicional, no mun<strong>do</strong> <strong>do</strong>s protocolos de comunicação,<br />

atribuir a designação de Esta<strong>do</strong>s.<br />

No cenário em causa, é pacífica a existência de <strong>do</strong>is<br />

Esta<strong>do</strong>s no receptor:<br />

- de um la<strong>do</strong>, o Esta<strong>do</strong> wLength: precede a chegada <strong>do</strong><br />

primeiro byte da PDUMC - e que só é aban<strong>do</strong>na<strong>do</strong> quan<strong>do</strong><br />

forem recebi<strong>do</strong>s to<strong>do</strong>s os (4) bytes <strong>do</strong> PDU-length;<br />

- por outro la<strong>do</strong>, o Esta<strong>do</strong> wLast: onde se ingressa com<br />

a chegada <strong>do</strong> último byte <strong>do</strong> PDU-length - e que perdura<br />

até chegar o último <strong>do</strong>s N bytes que compõem a PDUMC.<br />

Para bom entende<strong>do</strong>r, o receptor não permanece<br />

sempre no mesmo Esta<strong>do</strong>: ele irá evoluin<strong>do</strong> de um para<br />

outro Esta<strong>do</strong> - à medida que se forem suceden<strong>do</strong> eventos (no caso, os read após a chegada de bytes).<br />

Repare-se no prefixo w nos nomes <strong>do</strong>s Esta<strong>do</strong>s: recordam a palavra wait. Um Esta<strong>do</strong> não é defini<strong>do</strong> pelo<br />

que se "faz" nesse Esta<strong>do</strong>: é preferível pensá-lo como perío<strong>do</strong> de tempo em que não se faz nada! - senão<br />

esperar por algum evento, para decidir alguma acção - e, quan<strong>do</strong> é executada, assume-se que é instantânea!<br />

Nomeadamente,<br />

- wLength é o Esta<strong>do</strong> em que se espera que acabem de ser li<strong>do</strong>s<br />

to<strong>do</strong>s os (4) bytes <strong>do</strong> PDU-length; e, quan<strong>do</strong> isso acontecer, a leitura<br />

desse PDU-length é instantânea - tal como o é a passagem a wLast;<br />

- wLast é o Esta<strong>do</strong> em que se espera que acabe de ser li<strong>do</strong> o último<br />

byte da PDUMC; e, quan<strong>do</strong> isso acontecer, a invocação de TCP.ind é<br />

instantânea - tal como o é o regresso a wLength.<br />

A evolução entre os vários Esta<strong>do</strong>s pode ser representada de uma<br />

maneira apelativa (uma imagem vale mais que mil palavras…) por um<br />

Diagrama de Esta<strong>do</strong>s (fig. JdG-rVd). Neste,<br />

- os Esta<strong>do</strong>s são representa<strong>do</strong>s por círculos/elipses, etiqueta<strong>do</strong>s pelos<br />

seus Nomes, no caso wLength e wLast;<br />

- para representar as transições possíveis entre os Esta<strong>do</strong>s, usam-se<br />

"setas"; cada uma delas dirige-se <strong>do</strong> Esta<strong>do</strong> actual para o seguinte.<br />

- tais setas são etiquetadas por um rótulo com a estrutura<br />

Evento [,Predica<strong>do</strong>] / Acção


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 11 <strong>of</strong> <strong>39</strong><br />

No caso em apreço,<br />

- Primeiramente, há que ler os bytes que perfazem o PDU-length; depois, há que ler os N bytes que constituem o<br />

corpo da PDUMC. Pelo que será pacífica a concepção de uma Variável de Esta<strong>do</strong>, seja NMax, que memorize o<br />

número máximo de bytes expectáveis no Esta<strong>do</strong> em causa.<br />

- Esse valor NMax será fixa<strong>do</strong> (em 4 ou PDU-length) quan<strong>do</strong> se ingressa em wLength ou wLast - após o que irá<br />

decrescen<strong>do</strong> até zero, que é a condição de progresso para o outro Esta<strong>do</strong>. Pelo que será pacífica a concepção de um<br />

predica<strong>do</strong>, seja p1, que é o resulta<strong>do</strong> lógico (TRUE ou FALSE) da seguinte afirmação: "acabou de se ler os bytes<br />

que faltavam";<br />

- o Evento é o termo da execução <strong>do</strong> read invoca<strong>do</strong> no seguimento <strong>do</strong> sinal que anuncia a chegada de bytes da<br />

Internet;<br />

- a Acção é uma das seguintes: "iniciação de NMax", "decremento de NMax" e "invocação de TCP.ind".<br />

Concretamente (fig. JdG-rVd), admita-se que o receptor ingressou em wLength - ten<strong>do</strong> fixa<strong>do</strong> NMax=4.<br />

- se for sinalizada a chegada de bytes, o receptor invoca read (solicitan<strong>do</strong> a leitura de, até, 4 bytes); NMax é<br />

decrementa<strong>do</strong> <strong>do</strong> número de bytes efectivamente li<strong>do</strong>s;<br />

- se p1 não se verificar (~p1 representa "ainda não se acabou de ler os bytes que faltavam, 0


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 12 <strong>of</strong> <strong>39</strong><br />

O leitor poderá conferir que, em ambos os Esta<strong>do</strong>s, o evento read, quan<strong>do</strong> p1=FALSE, conduz à mesma<br />

Acção, a saber: "Decrementa NMax". Isso sugere o colapso desses Esta<strong>do</strong>s em um único, seja wData (fig. JdGrVf),<br />

pelo artifício de acrescentar uma Variável de Esta<strong>do</strong> mais, seja B, com o seguinte comportamento:<br />

- é iniciada a -1;<br />

- é fixada no tamanho, N, da PDUMT, quan<strong>do</strong> enfim se tiverem<br />

li<strong>do</strong> os primeiros 4 bytes;<br />

- e é reposta em -1 quan<strong>do</strong> se tiverem li<strong>do</strong> to<strong>do</strong>s os N bytes<br />

dessa PDU.<br />

Agora, existe somente um Esta<strong>do</strong> e, obviamente, uma seta<br />

apenas. Aquilo que muda é a Acção a executar aquan<strong>do</strong> <strong>do</strong> read:<br />

irá depender de p1 e de um novo predica<strong>do</strong>, a saber, p2 - que<br />

deverá ser o resulta<strong>do</strong> lógico da seguinte afirmação: "B é igual a -<br />

1".<br />

Com menos Esta<strong>do</strong>s, e menos setas, adivinha-se que o<br />

subsequente código será mais simples de desenhar, corrigir e<br />

manter…<br />

Nota: o objectivo da precedente discussão é somente a abordagem a conceitos como sejam: Esta<strong>do</strong>s,<br />

Transições, Predica<strong>do</strong>s, Acções, Diagramas e Tabelas de Esta<strong>do</strong>. Pelo que o Diagrama apresenta<strong>do</strong> em JdGrVd<br />

não há-de entender-se como definitivo. De facto, ele não funciona!<br />

- por um la<strong>do</strong>, considere-se, nomeadamente, o caso de, em wLast, chegarem NMax+4 bytes; no Diagrama, fazse<br />

uma transição para wLength; todavia, não terá escapa<strong>do</strong> ao leitor que, nessa situação, se deveria permanecer<br />

em wLast! e, se só tivessem chega<strong>do</strong> NMax+3 bytes, transitar-se-ia para wLength - mas NMax deveria ser inicia<strong>do</strong><br />

a 1!<br />

- por outro la<strong>do</strong>, pode configurar-se read como bloqueante - isto é: quan<strong>do</strong> se executa read, o Sistema<br />

Operativo "suspende" o programa - sem sequer conferir se o utiliza<strong>do</strong>r digitou entretanto algo no tecla<strong>do</strong>…<br />

Uma solução (já entrevista no comentário que acompanha JdG-rIIc) é: que o read recolha num buffer tu<strong>do</strong> o<br />

que chegou da rede e se analise depois esse buffer - não deven<strong>do</strong> ser difícil ao leitor alterar o Diagrama de<br />

Esta<strong>do</strong>s em conformidade…<br />

- enfim, está-se pressupon<strong>do</strong> que o read se volve sempre na leitura de ao menos um byte… Ora, tal não é<br />

verdade: se tiver havi<strong>do</strong> close por parte <strong>do</strong> interlocutor remoto, ou o seu crash, o read volve-se em 0 bytes…<br />

Nível Transparência, Diagrama <strong>do</strong> Transmissor:<br />

Ao receptor (<strong>do</strong> Cliente ou Servi<strong>do</strong>r), há-de então associar-se um<br />

Diagrama de Esta<strong>do</strong>s… mas, ao transmissor, também! Porquê? A<br />

interrogação é legítima: no fim de contas, de que é que necessita o<br />

transmissor recordar-se? não lhe bastará, quan<strong>do</strong> lhe é passada uma<br />

PDU, prefaciá-la de um PDU-length e invocar write?<br />

Não é bem assim! Quan<strong>do</strong> se invoca write, designan<strong>do</strong> um buffer<br />

com 4+N bytes, nem sempre isso conduz, de facto, ao envio para a<br />

Internet de to<strong>do</strong>s eles: o s<strong>of</strong>tware TCP pode limitar-se a enviar apenas<br />

os primeiros w bytes (com w≤4+N) - pelo que é preciso invocar outro<br />

write (sobre os restantes N-w bytes); e quiçá seja necessário outro(s)<br />

mais - até terem si<strong>do</strong> envia<strong>do</strong>s to<strong>do</strong>s os 4+N bytes.<br />

Com estes consideran<strong>do</strong>s, é pacífica a existência de <strong>do</strong>is Esta<strong>do</strong>s no<br />

transmissor (fig. JdG-rVg):<br />

- de um la<strong>do</strong>, o Esta<strong>do</strong> wFirst: é aquele que precede a invocação de<br />

TCP.req;<br />

- por outro la<strong>do</strong>, o Esta<strong>do</strong> wNet: é aquele onde se ingressa quan<strong>do</strong> o número de bytes escritos (com write) é<br />

inferior ao total a enviar.<br />

Quanto às restantes componentes <strong>do</strong> Diagrama de Esta<strong>do</strong>s,<br />

- agora, será pacífica a concepção de uma Variável de Esta<strong>do</strong>, seja R, que memorize o número de bytes que<br />

resta escrever;


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 13 <strong>of</strong> <strong>39</strong><br />

- esse valor R será fixa<strong>do</strong> (em 4+N) quan<strong>do</strong> se ingressa em wNet - após o que irá decrescen<strong>do</strong> até zero, que é a<br />

condição de reingresso em wFirst. Pelo que será pacífica a concepção de um predica<strong>do</strong>, seja p4, que é o resulta<strong>do</strong><br />

lógico (TRUE ou FALSE) da seguinte afirmação: "acabou de se enviar os bytes que faltavam, R==0";<br />

- o Evento pode ser um <strong>do</strong>s <strong>do</strong>is seguintes: a invocação de TCP.req e o termo, Swrite, da execução de um write;<br />

- a Acção é uma das seguintes: "iniciação de R", "decremento de R" e "invocação de write de bytes com destino<br />

à Internet".<br />

Concretamente (fig. JdG-rVg), admita-se que o transmissor ingressou em wFirst - ten<strong>do</strong> fixa<strong>do</strong> R=0.<br />

- se for invocada TCP.req, o transmissor acrescenta o PDU-length, fixa R=4+N e invoca write (solicitan<strong>do</strong> a<br />

escrita de R bytes); R é depois decrementa<strong>do</strong> <strong>do</strong> número de bytes efectivamente escritos;<br />

- se p4 se verificar, o Esta<strong>do</strong> permanece sen<strong>do</strong> wFirst (seta a Este de wFirst);<br />

- caso contrário ("0


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 14 <strong>of</strong> <strong>39</strong><br />

X.A - Preliminares: a Arquitectura Modular <strong>do</strong> Elemento de Utiliza<strong>do</strong>r<br />

A. No Cliente,<br />

- a leitura <strong>do</strong> tecla<strong>do</strong> e a impressão no ecrã estão a cargo <strong>do</strong> Gestor da Interface;<br />

- o estabelecimento e fecho da conexão estão a cargo <strong>do</strong> Gestor da Conexão;<br />

- o Gestor dum <strong>Jogo</strong> (GM, Game Manager) dedica-se à evolução de um jogo de acor<strong>do</strong> com:<br />

- os coman<strong>do</strong>s <strong>do</strong> utiliza<strong>do</strong>r;<br />

- os sinais (accept e close) provin<strong>do</strong>s <strong>do</strong> Gestor da Conexão;<br />

- as PDUs recebidas da Rede.<br />

- o Boss: conforme ao coman<strong>do</strong> digita<strong>do</strong> ou PDU recebida, sinaliza-o ao pertinente Gestor de <strong>Jogo</strong>.<br />

No Diagrama, esse sinal é prefixa<strong>do</strong> por '-'. Eventualmente, esse Gestor poderá devolver (return) alguma<br />

indicação,como seja: SIGPIPE (a conexão fin<strong>do</strong>u) ou End (um <strong>Jogo</strong> GId acabou); ela vem prefixada por '+'<br />

Para não sobrecarregar os Diagrama de Esta<strong>do</strong>s com predica<strong>do</strong>s "PDU ou Coman<strong>do</strong> inváli<strong>do</strong>",<br />

- a entrega de um coman<strong>do</strong> ao Boss é feita através de um Filtro de Coman<strong>do</strong>s que o verifica, alertan<strong>do</strong> o<br />

utiliza<strong>do</strong>r para algum erro nele detecta<strong>do</strong>;<br />

- uma PDU recebida é de imediato passada por um Filtro de PDUs, que a rejeita se for achada inválida.<br />

Considere-se um coman<strong>do</strong> genérico. O Gestor da Interface lê-o, valida-o - e, se for o caso, alerta para eventuais<br />

erros de sintaxe; se não for o caso, reporta-o ao Boss. Este também o valida - reportan<strong>do</strong>, se for o caso, eventuais<br />

erros (por ex., "Já se atingiu o número máximo de jogos" ou "O GId não existe"); se não for o caso, prossegue:<br />

- se o coman<strong>do</strong> começa um jogo novo (Start ou Join), reserva-lhe um descritor livre, seja Gf, e invoca sobre ele<br />

o Gestor de <strong>Jogo</strong>, passan<strong>do</strong> Start ou Join como argumento;<br />

Adiante, far-se-á recurso à expressão "corre E em X" para significar "o Boss invoca o Gestor <strong>do</strong> <strong>Jogo</strong> X,<br />

passan<strong>do</strong>-lhe E como argumento"; a frase anterior terminaria, então: "seja Gf, e corre esse coman<strong>do</strong> em Gf".<br />

- se se trata de Play, corre Play sobre o GId menciona<strong>do</strong>;<br />

- se se trata de Abort, transmite cAbort-Pdu; ademais, corre Abort nos GIds que respeitam a um <strong>Jogo</strong> local;<br />

- enfim, se se trata de List, envia a cList-Pdu.<br />

Para os casos <strong>do</strong>s coman<strong>do</strong>s Start, Join e Abort, o Boss contacta o Gestor de Conexão - que, se o não tiver<br />

feito ainda, enviará connect ao Servi<strong>do</strong>r. Quan<strong>do</strong> o accept chegar, o Gestor da Conexão passa-o ao Boss - que o irá<br />

correr em to<strong>do</strong>s os GIds; enuncia<strong>do</strong> análogo vale para a chegada de close da Rede.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 15 <strong>of</strong> <strong>39</strong><br />

Enquanto não chega o close, uma PDU que venha da Rede é entregue ao Boss; este valida-a e<br />

- se se trata de sGameList-Pdu ou sError-Pdu, passa o respectivo conteú<strong>do</strong> ao Gestor da Interface;<br />

- nos restantes casos, corre essa PDU no Gf ou GId a que ela diz respeito.<br />

Para bom entende<strong>do</strong>r: o Boss centra-se em eventos que não afectam nenhum jogo local (List ou mesmo Abort),<br />

ou que poderão afectar a mais que um, quiçá mesmo a to<strong>do</strong>s (close ou mesmo connect e Abort). Os Diagramas<br />

adiante têm então que cobrir a ambos, Boss e Gestor de <strong>Jogo</strong>, GM.<br />

B. No Servi<strong>do</strong>r,<br />

- a leitura e a escrita em disco estão a cargo <strong>do</strong> Gestor de Ficheiros;<br />

- o estabelecimento e fecho das conexões estão a cargo <strong>do</strong> Gestor de Conexões;<br />

- o Gestor de <strong>Jogo</strong> dedica-se à evolução de um jogo de acor<strong>do</strong> com:<br />

- as PDUs recebidas da Rede;<br />

- o expirar <strong>do</strong> Relógio TTTL;<br />

- os sinais (close) provin<strong>do</strong>s <strong>do</strong> Gestor da Conexões.<br />

- o Boss:<br />

- conforme à PDU recebida ou Relógio em causa, sinaliza-o ao(s) pertinente(s) Gestor(es) de <strong>Jogo</strong>(s).<br />

Considere-se a chegada de um connect. O Gestor de Conexões reserva um descritor para a nova conexão - e<br />

devolve accept. Um eventual close que venha da Rede é entregue ao Boss - que o irá sinalizar aos Gestores <strong>do</strong>s<br />

<strong>Jogo</strong>s que essa conexão suporta.<br />

Enquanto não chega o close, uma PDU que venha da Rede é entregue ao Boss, acompanha<strong>do</strong> da sua<br />

proveniência; este valida-a e<br />

- se se trata de cList-Pdu, devolve sGameList-Pdu;<br />

- se se trata de um cStart-Pdu, procura um descritor livre, seja GId, e corre a cStart-Pdu sobre ele;<br />

- nos restantes casos, determina o GId a que a PDU diz respeito, e corre a PDU sobre ele.<br />

Eventualmente, a PDU pode respeitar a mais que um par Cliente/Servi<strong>do</strong>r; é o caso de cPlay-Pdu e cAbort-Pdu.<br />

Pelo que levam à tomada de decisões (envio de sBoard-Pdu e sAbort-Pdu) que afectam outro par Cliente/Servi<strong>do</strong>r.<br />

Para bom entende<strong>do</strong>r: o Boss centra-se em eventos que não afectam nenhum Cliente específico (List), ou que<br />

envolvem mais que um jogo ou Cliente (close, cPlay-Pdu, cAbort-Pdu e o expirar <strong>do</strong> relógio TTTL).<br />

X.B - Eventos no <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Uma primeira questão é: quais os Eventos que provocam alterações em um par Cliente-Servi<strong>do</strong>r? Eventos são<br />

algo inespera<strong>do</strong>, "aleatorio". Consideran<strong>do</strong> a Arquitectura acima, o conjunto Elemento de Utiliza<strong>do</strong>r + Módulo de<br />

Comunicações deve ser sensível a três Classes de eventos:<br />

1. aqueles ocorri<strong>do</strong>s "em cima" - nomeadamente:<br />

- o "input" digita<strong>do</strong> pelo joga<strong>do</strong>r (Start, Join, etc.);<br />

- a tomada de decisões pelo Servi<strong>do</strong>r, a saber: o envio de Ok/Sorry, ou <strong>do</strong> Tabuleiro ou Listagem de jogos, o<br />

aborto de um <strong>Jogo</strong>, o fecho da conexão, etc.<br />

2. aqueles ocorri<strong>do</strong>s "em baixo" -, nomeadamente:<br />

- para passar as PDUs provenientes da Internet: cStart-Pdu, cList-Pdu, etc.<br />

- para sinalizar fenómenos detecta<strong>do</strong>s pelo s<strong>of</strong>tware TCP/UDP, como seja:<br />

- a chegada de connect/accept ou close;<br />

3. os restantes - nomeadamente:<br />

- os que correspondem ao expirar de relógios, como seja: aquele, TConnext, associa<strong>do</strong> ao connect, e aquele,<br />

TimeToLive (TTTL), associa<strong>do</strong> à recepção de cStart-Pdu, cJoin-Pdu e cPlay-Pdu;<br />

- iniciação, eventualmente após crash da própria máquina…<br />

- o "signal" provoca<strong>do</strong> pelo write em um socket-TCP fecha<strong>do</strong> (SIGPIPE)…<br />

- a indicação "End" provocada pelo acabar de um <strong>Jogo</strong>…<br />

No Anexo-A.4a, condensaram-se num quadro os eventos significativos para cada parte: Cliente e Servi<strong>do</strong>r.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 16 <strong>of</strong> <strong>39</strong><br />

Nota: por comodidade, o texto adiante assume sincronismo entre a chegada de PDUs e a subsequente<br />

tomada de decisões <strong>do</strong> Servi<strong>do</strong>r. Por outras palavras: quan<strong>do</strong>, por ex., chega uma cList-Pdu ao Servi<strong>do</strong>r,<br />

assume-se que o tempo que ele demora a validar a PDU e a decidir enviar a Listagem de <strong>Jogo</strong>s é zero! A<br />

consequência disso é: haven<strong>do</strong> que, na Tabela de Esta<strong>do</strong>s, dedicar uma linha ao evento "chegada de cList-<br />

Pdu", essa mesma linha cobre o evento "o Servi<strong>do</strong>r decidiu enviar uma sGameList-Pdu com a Listagem".<br />

X.C - Esta<strong>do</strong>s no <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Para a execução <strong>do</strong>s coman<strong>do</strong>s Start, Join, Play e Abort, exige-se uma conexão-TCP entre Cliente e Servi<strong>do</strong>r.<br />

Em ordem ao seu estabelecimento, o Cliente precisa enviar connect - deven<strong>do</strong> o Servi<strong>do</strong>r responder-lhe com<br />

accept. Com este pano de fun<strong>do</strong>, serão pacíficas as asserções seguintes:<br />

No Boss <strong>do</strong> Cliente, convirá definir três Esta<strong>do</strong>s, com os significa<strong>do</strong>s adiante:<br />

- closed: ainda não se iniciou o estabelecimento de uma conexão-TCP com o Servi<strong>do</strong>r;<br />

- wAccept: ten<strong>do</strong> o joga<strong>do</strong>r invoca<strong>do</strong> Start, Join ou Abort, o Cliente enviou connect, está aguardan<strong>do</strong> accept;<br />

- cConnected: já foi recebi<strong>do</strong> accept após ter-se envia<strong>do</strong> connect.<br />

No Servi<strong>do</strong>r, convirá, por cada Cliente, definir <strong>do</strong>is Esta<strong>do</strong>s, com os significa<strong>do</strong>s adiante<br />

- wConnect: ainda não se concluiu o estabelecimento de uma conexão-TCP com esse Cliente;<br />

- sConnected: já foi envia<strong>do</strong> accept após se ter recebi<strong>do</strong> connect.<br />

X.D - Diagramas de Esta<strong>do</strong>s no <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Nos Anexos A.4b e A.4c, listam-se Variáveis de Esta<strong>do</strong> e Predica<strong>do</strong>s que convirá definir para o <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>.<br />

Diagramas de Esta<strong>do</strong> no Cliente: o Boss<br />

Em JdG-rXc (no Anexo-A.4b), apresenta-se o Diagrama de Esta<strong>do</strong>s <strong>do</strong> Boss - para os casos em que é<br />

irrelevante o Esta<strong>do</strong> em que ele se encontra:<br />

- o joga<strong>do</strong>r digita Start ou Join, mas o Cliente recorda-se que ele já embarcou em 4 <strong>Jogo</strong>s (p10): apresenta<br />

Sorry no ecrã …<br />

- o joga<strong>do</strong>r digita List; pelo que se fixa ListReqed=TRUE e se envia a cList-Pdu; em caso de sucesso, virá a<br />

receber a sGameList-Pdu, viabilizan<strong>do</strong> a impressão no ecrã da listagem <strong>do</strong>s <strong>Jogo</strong>s em curso (no Sistema).<br />

Em JdG-rXd (no Anexo-A.4b), apresenta-se o Diagrama <strong>do</strong> Boss para o caso p10=FALSE:<br />

- em closed, o joga<strong>do</strong>r pode digitar Start ou Join. O Cliente:<br />

- determina o menor valor Gf cujo Role seja -1, incrementa Gc; corre o coman<strong>do</strong> digita<strong>do</strong> sobre Gf;<br />

- envia connect ao Servi<strong>do</strong>r, arma o TimeOut Tc e transita para wAccept;<br />

- em closed, o joga<strong>do</strong>r pode também digitar Abort. O Cliente:<br />

- salvaguarda em AList o(s) GId(s) associa<strong>do</strong>s a Abort;<br />

- envia connect ao Servi<strong>do</strong>r, arma o TimeOut Tc e transita para wAccept;<br />

- em closed, o joga<strong>do</strong>r pode digitar Play.<br />

- o Cliente apresenta Sorry no ecran;<br />

- em wAccept, há que prever o caso de o joga<strong>do</strong>r ser o Lucky Luke (precisamente ele! - que dispara mais rápi<strong>do</strong><br />

que a própria sombra…): ten<strong>do</strong> já digita<strong>do</strong> Start, Join ou Abort, e antes de chegar accept, invoca um desses três<br />

coman<strong>do</strong>s…<br />

O Cliente procede como em closed - com a excepção de que, agora, já não envia connect nem arma o Tc.<br />

- em wAccept, o joga<strong>do</strong>r pode digitar Play.<br />

- o Cliente apresenta Sorry no ecran;<br />

- em wAccept, o Cliente pode receber accept. Cancela Tc e:<br />

Se a AList não estiver vazia, isto é, o joga<strong>do</strong>r digitou um ou mais Abort (p11):


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 17 <strong>of</strong> <strong>39</strong><br />

- por cada GId nela salvaguarda<strong>do</strong>, envia a correspondente cAbort-Pdu;<br />

- para cada GId que for um <strong>Jogo</strong> local (na prática, um que tenha si<strong>do</strong> associa<strong>do</strong> a um Join), corre Abort<br />

sobre ele;<br />

- enfim, esvazia a AList;<br />

Se Gc>0 (p17), o Cliente corre accept em cada um <strong>do</strong>s Gs:<br />

- na prática, consoante seja S ou J, assim o Gestor de <strong>Jogo</strong>s envia a cStart-Pdu ou a cJoin-Pdu;<br />

Na cStart-Pdu, deve inscrever-se o pertinente Gf, que deve ser ecoa<strong>do</strong> pelo Servi<strong>do</strong>r na<br />

correspondente sStart-Pdu<br />

Enfim:<br />

- se algum <strong>do</strong>s Role não é -1 (p17), transita para cConnected;<br />

- caso contrário (~p17), envia close, efectua cInit (inicia o Cliente) e transita para closed.<br />

- em wAccept, o Timeout pode expirar (o accept não vem ou atrasou-se demasia<strong>do</strong>):<br />

Imprime um Warning, envia close, corre Abort em cada um <strong>do</strong>s Gs{}, efectua cInit e transita para closed;<br />

- em wAccept, pode receber-se close:<br />

Cancela Tc, imprime um Warning, corre Abort em cada um <strong>do</strong>s Gs{}, efectua cInit e transita para closed;<br />

- em cConnected, o joga<strong>do</strong>r pode digitar Start, Join ou Abort.<br />

O Cliente procede como em wAccept; no caso de Abort envia de imediato a cAbort-Pdu.<br />

- em cConnected, pode receber-se close:<br />

O Cliente imprime um Warning, corre Abort em cada um <strong>do</strong>s Gs{}, efectua cInit e transita para closed;<br />

- em cConnected, o joga<strong>do</strong>r pode digitar Play.<br />

- se o <strong>Jogo</strong> referi<strong>do</strong> não está em curso (p13), apresenta Sorry no ecran; caso contrário, corre Play nesse GId;<br />

- em cConnected, podem ser recebidas sStart-Pdu e sJoin-Pdu, sPlay-Pdu e sBoard-Pdu e sAbort-Pdu e sError-<br />

Pdu;<br />

- se a sJoin-Pdu, sPlay-Pdu ou sBoard-Pdu se referir a GId que não está em curso localmente (p13), é<br />

ignorada; caso contrário, corre-se essa PDU sobre esse GId;<br />

- se sStart-Pdu se referir a Gf que não está em curso (p22), é ignorada; caso contrário corre-se a sStart-Pdu<br />

sobre esse Gf;<br />

- se se receber sAbort-Pdu,<br />

- se algum GId que transportar ainda estiver em curso, corre-se sAbort-Pdu sobre esse GId;<br />

- se se receber sError-Pdu, é apresentada a Mensagem no ecran.<br />

Eventualmente, algumas das Acções submetidas ao Gestor de um <strong>Jogo</strong> GId (como sejam -Abort e -sBoard)<br />

poderão sinalizar, com +End, o seu termo. A ser o caso, decrementa-se Gc e fixa-se Role=-1 em GId.<br />

Diagramas de Esta<strong>do</strong>s no Cliente: o Gestor dum <strong>Jogo</strong><br />

A partir <strong>do</strong>s Diagramas Temporais, o desenho <strong>do</strong> Gestor dum <strong>Jogo</strong> num Cliente é pacífico. Antes de mais,<br />

atribuem-se etiquetas, nas verticais que representam o Cliente, aos momentos de espera, isto é, àqueles que<br />

precedem eventos significativos. Como ex., em JdG-rVIh re-desenha-se a fig JdG-rVIg (que esquematiza o começo<br />

dum jogo entre <strong>do</strong>is joga<strong>do</strong>res, W e), todavia agora refeita com essas etiquetas (sublinhadas):


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 18 <strong>of</strong> <strong>39</strong><br />

Na prática, cada etiqueta acaba por ser o Nome de um Esta<strong>do</strong>; concretamente:<br />

- antes de Start (ou de Join), ambos os Clientes se encontram no Esta<strong>do</strong> closed;<br />

- quan<strong>do</strong> o joga<strong>do</strong>r W digita Start, o Cliente invoca connect e transita para wAccept (espera accept);<br />

- quan<strong>do</strong> o accept chega, o Cliente envia cStart-Pdu e transita para wsStart (espera a sStart-Pdu);<br />

- com a chegada de sStart-Pdu, o Cliente transita para wsBoard (espera a sBoard-Pdu);<br />

- quan<strong>do</strong> o joga<strong>do</strong>r E digita Join, o Cliente invoca connect e transita para wAccept (espera accept);<br />

- quan<strong>do</strong> o accept chega, o Cliente envia cJoin-Pdu e transita para wsJoin (espera a sJoin-Pdu);<br />

- com a chegada de sJoin-Pdu, o Cliente transita para wPlay (espera uma jogada, Play);<br />

- quan<strong>do</strong> o joga<strong>do</strong>r E digita uma jogada Play, o Cliente envia cPlay-Pdu e transita para wsPlay;<br />

- com a chegada de sPlay-Pdu, o Cliente transita para wsBoard;<br />

- com a chegada de sBoard-Pdu a E, o Cliente mantém-se em wsBoard;<br />

- com a chegada de sBoard-Pdu a W, o Cliente transita para wPlay;<br />

- quan<strong>do</strong> o joga<strong>do</strong>r W digita uma jogada Play, o Cliente envia cPlay-Pdu e transita para wsPlay;<br />

- com a chegada de sPlay-Pdu, o Cliente transita para wsBoard;<br />

- com a chegada de sBoard-Pdu a W, o Cliente mantém-se em wsBoard;<br />

- com a chegada de sBoard-Pdu a E, o Cliente transita para wPlay;<br />

-etc…<br />

Repare-se:<br />

- em ambos os Clientes, o envio de connect é acompanha<strong>do</strong> da transição para wAccept; mas, quan<strong>do</strong> o accept<br />

chega, W envia cStart-Pdu e E envia cJoin-Pdu. Para diferenciar este comportamento, convém uma variável-de-<br />

Esta<strong>do</strong>, Role; é iniciada a -1 e, ao ingressar em wAccept, W fixa-a em Role=S e E fixa-a em Role=J;<br />

- <strong>do</strong> mesmo mo<strong>do</strong>, com a chegada <strong>do</strong> tabuleiro, em wsBoard, a vez de jogar virá a caber somente a um <strong>do</strong>s<br />

joga<strong>do</strong>res; para o efeito, convém uma variável-de-Esta<strong>do</strong>, MyTurn; é iniciada a FALSE ou TRUE, consoante Role<br />

for S ou J, e, sempre que um Cliente recebe o tabuleiro, ela deve mudar de valor.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 19 <strong>of</strong> <strong>39</strong><br />

Com o que fica dito, é pacífico o desenho <strong>do</strong> Diagrama de Esta<strong>do</strong>s correspondente ao Diagrama Temporal<br />

acima, cf. JdG-rXa:<br />

Repare-se:<br />

- o esta<strong>do</strong> inicial é closed;<br />

- com o coman<strong>do</strong> Start (e pressuposto que já se enviou connect), fixa-se Role e transita-se para wAccept;<br />

dever-se-á salvaguardar MyName (e, se for o caso de Join, GId);<br />

- com a chegada de accept, e consoante o valor de Role, envia-se cStart-Pdu ou cJoin-Pdu, e transita-se<br />

respectivamente para wsStart e wsJoin;<br />

- e assim por diante…<br />

Deve sublinhar-se: este é o Diagrama de Esta<strong>do</strong>s de um <strong>Jogo</strong>. Um droga<strong>do</strong> <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> poderá ter<br />

embarca<strong>do</strong> em vários jogos - e a cada um corresponde um Diagrama assim. Num deles, o Cliente poderá encontrarse<br />

no Esta<strong>do</strong> wPlay e em outro encontrar-se, por ex., em wsBoard ou wsJoin..<br />

Entretanto, deve também sublinhar-se: JdG-rXa teve em conta apenas o Diagrama Temporal em JdG-rVIg -<br />

pelo que se pode temer que esteja incompleto! Para o completar, a filos<strong>of</strong>ia é a mesma:<br />

- inscrever, nos demais Diagramas Temporais, etiquetas nomean<strong>do</strong> os Esta<strong>do</strong>s - isto é, as situações de espera -,<br />

e repercutir no Diagrama de Esta<strong>do</strong>s acima o comportamento que aqueles Diagramas determinam;<br />

- de qualquer mo<strong>do</strong>, e porquanto quiçá não se tenham desenha<strong>do</strong> Diagramas Temporais para todas as situações<br />

passíveis de ocorrer, repetir até à exaustão a seguinte meto<strong>do</strong>logia:<br />

a. interrogar-se: para cada Esta<strong>do</strong>, quais os eventos passíveis de ocorrer?<br />

b. investigar: para cada um desses eventos, qual o comportamento que o Cliente deve observar?<br />

c. e repercutir no Diagrama de Esta<strong>do</strong>s acima esse comportamento.<br />

Com o que fica dito, será pacífico o desenho final <strong>do</strong> Gestor de um <strong>Jogo</strong> em JdG-rXb (no Anexo A.4b):<br />

Diagramas de Esta<strong>do</strong> no Servi<strong>do</strong>r: o Boss<br />

Em JdG-rIXd (no Anexo-A.4c), apresenta-se o Diagrama de Esta<strong>do</strong>s <strong>do</strong> Boss para o caso em que:<br />

- se recebe a cList-Pdu; pelo que é enviada a sGameList-Pdu, com a listagem <strong>do</strong>s <strong>Jogo</strong>s em Curso: GId e<br />

respectivos Names;<br />

Em JdG-rIXe (no Anexo-A.4c), apresenta-se o Diagrama <strong>do</strong> Boss para os restantes casos:<br />

- em wConnect, pode receber-se connect de um socket…<br />

- o Servi<strong>do</strong>r deverá iniciar a respectiva SockGIds e devolver accept; e o Esta<strong>do</strong> dessa conexão deverá<br />

transitar para sConnected.<br />

- em sConnected, o Servi<strong>do</strong>r pode receber cStart-Pdu…<br />

- o Servi<strong>do</strong>r deverá, se já existem 4 jogos em curso (p10), devolver sStart-Pdu(Sorry,Gf);


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 20 <strong>of</strong> <strong>39</strong><br />

- caso contrário, deverá determinar o valor GId cujo All[GId].Names[S] esteja vazio e tenha si<strong>do</strong> menos<br />

recentemente usa<strong>do</strong>, e correr cStart-Pdu sobre esse GId;<br />

- em sConnected, o Servi<strong>do</strong>r pode receber cJoin-Pdu…<br />

- o Servi<strong>do</strong>r deverá, se o GId referi<strong>do</strong> não estiver em curso ou se já forem conheci<strong>do</strong>s os seus <strong>do</strong>is joga<strong>do</strong>res<br />

(p31), devolver sJoin-Pdu(Sorry);<br />

- caso contrário, deverá correr cJoin-Pdu sobre esse GId.<br />

- em sConnected, o Servi<strong>do</strong>r pode receber cPlay-Pdu…<br />

- o Servi<strong>do</strong>r deverá, se o GId referi<strong>do</strong> não estiver em curso (p32), devolver sJoin-Pdu(Sorry);<br />

- caso contrário, deverá correr cPlay-Pdu sobre esse GId.<br />

- em sConnected, o Servi<strong>do</strong>r pode receber cAbort-Pdu…<br />

O Servi<strong>do</strong>r varre os GIds referi<strong>do</strong>s: para cada um,<br />

- se não estiver em curso (p36), devolve sError-Pdu(Reason);<br />

- caso contrário, deverá correr -Abort sobre esse GId;<br />

- em sConnected, pode receber-se close:<br />

Varre a SockGIds, procuran<strong>do</strong> <strong>Jogo</strong>s em curso sobre a conexão: por cada um, corre -Abort sobre ele;<br />

- em sConnected, aquan<strong>do</strong> <strong>do</strong> processamento de cPlay-Pdu, cAbort-Pdu, expirar <strong>do</strong> Timer ou close, o Boss<br />

poderá receber <strong>do</strong> Gestor de <strong>Jogo</strong>s uma notificação, +End, de que um <strong>Jogo</strong> acabou. Deverá verificar, para cada uma<br />

das conexões que o suportam, se ele "era o último <strong>Jogo</strong>"; se o for (e não estiver ainda fechada), executa close sobre<br />

essa conexão.<br />

Diagramas de Esta<strong>do</strong>s no Servi<strong>do</strong>r: o Gestor dum <strong>Jogo</strong><br />

Seguin<strong>do</strong> a mesma filos<strong>of</strong>ia seguida para o Gestor dum <strong>Jogo</strong> no Cliente, pode agora traçar-se o Diagrama de<br />

Esta<strong>do</strong>s <strong>do</strong> Gestor dum <strong>Jogo</strong> no Servi<strong>do</strong>r, cf. JdG-rIXc (no Anexo-A.4c):<br />

XI - Tabela de Esta<strong>do</strong>s <strong>do</strong> Nível Superior <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Nos Anexos A.4d e A.4e, apresentam-se as Tabelas de Esta<strong>do</strong> <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> - naturalmente extraídas <strong>do</strong>s<br />

Diagramas de Esta<strong>do</strong> precedentes. Ocasionalmente,<br />

- é invoca<strong>do</strong> Sorry, significan<strong>do</strong> que deve ser apresentada uma mensagem de Alerta no ecran;<br />

- nas quadrículas vazias dever-se-á invocar um procedimento Trace, que deverá inscrever num ficheiro "log" o<br />

evento ocorri<strong>do</strong>, para se poder reflectir sobre ele: pode ser um erro a corrigir necessariamente…<br />

XII - Estrutura das PDUs <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Do que fica dito, será pacífico estruturar as PDUs, cf. Anexo-A.5.<br />

Elas incluem um conjunto de Campos - cuja estrutura poderá ser, por ex.:<br />

MyName, Reason: sequências de códigos Ascii de caracteres terminadas por ‘/0’.<br />

Nota: a adição <strong>do</strong> PDU-length para efeitos de transparência/delimitação-de-PDUs garante que o receptor sabe<br />

onde terminam, nomeadamente, a cStart-Pdu e a cJoin-Pdu – pelo que é desnecessário terminar MyName com ‘/0’.<br />

Enuncia<strong>do</strong> análogo vale para os campos Reason em sStart-Pdu, sJoin-Pdu, sPlay-Pdu e sAbort-Pdu.<br />

Mas isso já não é bem assim para o caso de sError-Pdu – pois que, em virtude da cAbort-Pdu a que ele se deve,<br />

pode referir mais que um GId. Neste caso, é mesmo necessário delimitar o campo Reason – intercalan<strong>do</strong> um ‘/0’<br />

antes da subsequente sucessão de GIds.<br />

GId, Gf, Line e Column: um octeto cada um, veiculan<strong>do</strong> um inteiro.<br />

Nota: esta representação é problemática: o Cliente e o Servi<strong>do</strong>r podem representar inteiros de maneira<br />

diferente; por ex., 01(16) pode, para um deles, significar o inteiro “1”, e, para o outro, significar "128". Na prática, o


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 21 <strong>of</strong> <strong>39</strong><br />

programa<strong>do</strong>r-em-C deve recorrer às funções htons (ou htonl) e ntohs (ou ntohl): antes de enviar um inteiro, deve<br />

convertê-lo na representação “net”, e, após recebê-lo, convertê-lo para a representação “host” local.<br />

Symbol: o código Ascii de “X” ou “O”<br />

Board: <strong>of</strong>erecem-se várias possibilidades; eis algumas delas:<br />

B.1 Uma sequência de Xs e Os, e Ls (estes últimos para representar casas-Livres). Por ex., o tabuleiro figura<strong>do</strong><br />

em JdG-rVIIb seria representa<strong>do</strong> por LXOLXLLLL: sucessivamente, assinala-se o conteú<strong>do</strong> das três casas da<br />

primeira linha, depois o das três casas da segunda linha, etc..<br />

B.2 Uma sequência de trios símbolo-linha-coluna – contemplan<strong>do</strong> apenas as casas já ocupadas. Para o mesmo<br />

tabuleiro, seria agora: X12O13X22. Este méto<strong>do</strong> seria mais eficiente que o anterior só até à terceira jogada,<br />

exclusive.<br />

B.3 Uma sequência X[LinhaColuna…]O[LinhaColuna…]. Para o mesmo tabuleiro, seria<br />

agora X1222O13. Repare-se que isto “funciona” pois nenhuma linha tem o valor O.<br />

List: Para representar a Listagem <strong>do</strong>s <strong>Jogo</strong>s já “Started”, uma possibilidade seria: uma<br />

sucessão de trios (ou apenas tuplos) consistin<strong>do</strong> em:<br />

GId, segui<strong>do</strong> de SName e, eventualmente, <strong>do</strong> JName<br />

Será esta representação problemática? Repare-se: os primeiros <strong>do</strong>is campos recebi<strong>do</strong>s serão GId (um inteiro) e<br />

SName (termina<strong>do</strong> por ‘/0’). Que virá a seguir? Se o byte a seguir ao ‘/0’ é um número (entre 1 e 4), então trata-se<br />

de um novo GId, caso contrário, trata-se <strong>do</strong> JName.<br />

Este raciocínio já não é isento de erros se o número máximo de jogos for, por ex., de 256 (e não apenas 4)! É<br />

que o byte após o ‘/0’ pode então ser interpreta<strong>do</strong> como um número ou como o código Ascii de um caracter (o das<br />

Maiúsculas estende-se entre 65 e 90)! Para que o Cliente saiba discernir entre os <strong>do</strong>is significa<strong>do</strong>s, há pois que<br />

acrescentar algo… Discutem-se adiante quatro possibilidades:<br />

More-Flag:<br />

No fun<strong>do</strong>, quan<strong>do</strong> o Cliente acaba de ler o ‘/0’ que finda o primeiro MyName, assalta-o uma dúvida: haverá<br />

ainda mais algum campo MyName? O Servi<strong>do</strong>r pode esclarecê-lo: logo após o ‘/0’, acrescenta uma flag, com <strong>do</strong>is<br />

valores possíveis, ‘1’, e ‘0’, significan<strong>do</strong> respectivamente que há/não-há mais nenhum campo MyName. Então,<br />

pela inspecção dessa flag, o Cliente sabe se, sim ou não, se deve preparar para ler um segun<strong>do</strong> campo MyName:<br />

Concretamente, para veicular a informação GId=2{Costa}GId=3{Luís,Catarina}, poderia enviar-se:<br />

2 Costa ‘/0’ (MF=)0 3 Luis ‘/0’ (MF=)1 Catarina ‘/0’<br />

Field-length:<br />

Repare-se: acima, é necessário enviar o termina <strong>do</strong>r ‘/0’: senão, o Cliente não sabe discernir nem a dimensão<br />

<strong>do</strong>s campos MyName (no ex., preenchi<strong>do</strong>s com Costa, Luís e Catarina) nem, por consequência, o local onde se<br />

encontra a More-Flag. Se em nomes de pessoas (sem acentos e outros quiproquós) isso será pacífico, já se torna<br />

problemático se se enviasse, por ex., a fotografia <strong>do</strong>s joga<strong>do</strong>res: poderia acontecer que, no seio daqueles três<br />

campos, viesse a aparecer o byte ‘/0’ – com a consequência óbvia: uma recepção errónea… Para o evitar, uma<br />

solução é: preceder MyName de um byte veiculan<strong>do</strong> o seu comprimento. Agora, a PDU para o caso acima seria:<br />

2 5 Costa 0 3 4 Luis 8 Catarina<br />

Repare-se: a seguir a “Costa” aparece 0, que significa: o campo seguinte (o segun<strong>do</strong> MyName) espraia-se por 0<br />

bytes, isto é: não existe.<br />

Termina<strong>do</strong>r e Character-stuffing:<br />

O méto<strong>do</strong> anterior de delimitação de um campo s<strong>of</strong>re de duas vulnerabilidades:<br />

- por um la<strong>do</strong>, MyName não pode exceder 255 caracteres; mas isso, dirá o leitor, e com razão, resolve-se<br />

reservan<strong>do</strong> para o Field-length mais que um byte: por ex., se lhe forem dedica<strong>do</strong>s <strong>do</strong>is bytes, esse campo poderá<br />

estender-se até cerca de 64 Kcaracteres…<br />

- por outro la<strong>do</strong>, para preencher o Field-length é necessário conhecer de antemão a dimensão <strong>do</strong> campo em<br />

questão; ora, isso nem sempre acontece. Ou mais precisamente: para o conhecer, há que gastar um certo tempo –


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 22 <strong>of</strong> <strong>39</strong><br />

atrasan<strong>do</strong> portanto o começo <strong>do</strong> envio da informação. Uma solução é utilizar um caracter termina<strong>do</strong>r a justapor ao<br />

campo, seja ‘t’. Agora, a PDU para o ex. acima seria:<br />

2 Costa t t 3 Luis t Catarina t<br />

Esse méto<strong>do</strong> é problemático se o campo em questão (MyName) puder conter esse termina<strong>do</strong>r - a consequência<br />

será óbvia: uma recepção errónea…Para o superar, uma solução é: quan<strong>do</strong> o ‘t’ deve ser interpreta<strong>do</strong> como<br />

termina<strong>do</strong>r, e apenas então, o Servi<strong>do</strong>r deve precedê-lo de um sinal, seja ‘s’. Mas, dir-se-á: e se aquele campo<br />

contiver precisamente a sequência ‘st’? – é que a consequência será óbvia: uma recepção errónea… Uma solução,<br />

dita de character stuffing, é: se um ‘s’ deve ser entendi<strong>do</strong> como byte de da<strong>do</strong>s (e não como byte de sinalização), ele<br />

deve ser duplica<strong>do</strong>. Com isso, a PDU para o ex. acima seria, antes:<br />

2 Cossta st st 3 Luiss st Catarina st<br />

Repare-se: os 's' em Costa e Luis aparecem em duplica<strong>do</strong>; e o 't' em Catarina não é precedi<strong>do</strong> de 's'. O Cliente<br />

seguirá uma filos<strong>of</strong>ia óbvia: se o ‘s’ aparecer duplica<strong>do</strong>, considera o primeiro como sen<strong>do</strong> “de da<strong>do</strong>s”, e remove o<br />

segun<strong>do</strong>; mas, se não aparecer duplica<strong>do</strong>, deve ser entendi<strong>do</strong> como “sinal” preceden<strong>do</strong> o termina<strong>do</strong>r.<br />

Field-type:<br />

Os três méto<strong>do</strong>s acima concernem a dimensão <strong>do</strong> campo JName. Mas há um ponto de vista alternativo: no<br />

fun<strong>do</strong>, o Cliente pretende saber se o byte após o primeiro MyName deve ser li<strong>do</strong> como GId ou como início de outro<br />

MyName; por outras palavras, o Cliente pretende saber qual o tipo de informação a ler: inteiro ou caracter? Eis<br />

uma solução: preceder cada valor de um Field-type.<br />

No exemplo entre mãos, podem definir-se os seguintes Field-types:<br />

GId-type=1 SName-type=2 JName-type=3<br />

Então, para veicular a informação GId=2{Costa}GId=3{Luís,Catarina], poderia enviar-se<br />

GId-type GId SName-type Length Value JName-type Length Value<br />

1 2 2 5 Costa<br />

1 3 2 4 Luis 3 8 Catarina<br />

O Cliente limita-se a, antes de recolher um valor, determinar o que está len<strong>do</strong>: um GId ou um MyName…<br />

Abra-se um parêntesis: os campos MyName acabam por ser representa<strong>do</strong>s por uma assim denominada<br />

estrutura TLV (Type/Lenth/Value). Intui-se que será a mais indicada para situações em que os campos não<br />

seguem necessariamente uma ordem pré-definida ou são opcionais.<br />

XIII - Apêndice: o Coman<strong>do</strong> List<br />

O coman<strong>do</strong> List (fig JdG-rVIe) foi trata<strong>do</strong> admitin<strong>do</strong> que o número máximo de <strong>Jogo</strong>s em curso é 4 - pelo que a<br />

listagem enviada numa sGameList-Pdu cabe dentro <strong>do</strong>s limites aceitáveis por UDP (algo menos que 64 Kbytes)…<br />

É então legítima a pergunta: e se aquele número puder ser bem maior? Claramente, a resposta por parte <strong>do</strong> Servi<strong>do</strong>r<br />

terá que se repartir por várias PDUs… - e, a manter-se o suporte "UDP", isso obriga a cuida<strong>do</strong>s extra: quiçá nem<br />

todas elas cheguem ao Cliente, e as que o fizerem talvez não o façam pela ordem em que foram transmitidas...<br />

É importante proporcionar ao joga<strong>do</strong>r a maior rapidez na visualização da listagem; para o efeito, convirá que, à<br />

medida que as PDUs cheguem ao Cliente, sejam de imediato apresentadas no ecran - sem por conseguinte esperar<br />

por aquelas que foram transmitidas anteriormente. Para que essa listagem seja coerente, cada PDU deverá veicular<br />

um conjunto fecha<strong>do</strong> de <strong>Jogo</strong>s - em vez de, a respeito de um GId, uma PDU referir o SName, e o respectivo<br />

JName seja referi<strong>do</strong> somente na PDU seguinte: é que não se sabe se alguma das PDUs se perderá pelo caminho…<br />

Para o efeito, deve ser o Elemento de Utiliza<strong>do</strong>r - que não o Módulo de Comunicações - a repartir a Listagem<br />

em várias PDUs (fig JdG-rVIi )…


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 23 <strong>of</strong> <strong>39</strong><br />

E há que definir a estrutura da sGameList-Pdu, por ex.,<br />

- o código da sGameList-Pdu, 12;<br />

- uma sequência de:<br />

- um GId, sob a forma de um inteiro;<br />

- um SName-Length, representan<strong>do</strong> o comprimento <strong>do</strong> SName;<br />

- esse SName;<br />

- um JName-Length, representan<strong>do</strong> o comprimento <strong>do</strong> JName associa<strong>do</strong> ao Join;<br />

- esse JName (se o JName-length for não-nulo);<br />

- uma Flag "MG", que será "1" se, e apenas se, a seguir ao <strong>Jogo</strong> corrente, se segue a especificação de<br />

mais outro <strong>Jogo</strong>.<br />

Eis um exemplo:<br />

12 4 5 Costa 8 Catarina 1 5 4 Joao 0 1 6 4 Luis 5 Maria 0<br />

Nota: o enuncia<strong>do</strong> postula que a Listagem seja suportada por UDP; se fora suportada por TCP, poder-se-ia<br />

acrescentar às PDUs algo que ajudasse o Cliente a perceber quan<strong>do</strong> é que a Listagem termina. Da discussão<br />

precedente, adivinham-se quatro possibilidades:<br />

- terminar cada sGameList-Pdu com uma MoreFlag - que só é 0 na última sGameList-Pdu…<br />

- incluir na primeira sGameList-Pdu um campo veiculan<strong>do</strong> o número de sGameList-Pdu que se irão enviar…<br />

- findar a última sGameList-Pdu com um termina<strong>do</strong>r de listagem, seja 'T', sinalizá-lo com 's' - e aplicar<br />

character stuffing…<br />

- conceber um novo tipo de PDUs, seja sEnd-Pdu, com o código 13, sem quaisquer parâmetros - e enviá-la logo<br />

após a última sGameList-Pdu…


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 24 <strong>of</strong> <strong>39</strong><br />

Anexo A: Especificação <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

A.1: Arquitectura de Comunicações<br />

A arquitectura de comunicações <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> des<strong>do</strong>bra-se em (fig JdG-rIVa):<br />

- um nível inferior, materializa<strong>do</strong> no par TCP/UDP, a cargo <strong>do</strong> Sistema Operativo;<br />

- um nível Transparência, ou de delimitação de PDUs, que converte invocações ("ecos" de PLAY.req,<br />

PLAY.ind, etc.) orientadas às PDUs em invocações (write, read) orientadas ao "Fluxo de octetos" - e vice-versa;<br />

- o Módulo de Comunicações, que empacota as PDUs e as passa ao nível Transparência - e vice-versa;<br />

- o elemento utiliza<strong>do</strong>r, no nível superior, que faz a interface:<br />

- no Cliente, com o terminal e o ecran através de que o Utiliza<strong>do</strong>r interage;<br />

- no Servi<strong>do</strong>r, com o sistema de ficheiros onde se regista a evolução <strong>do</strong>s eventos;<br />

- em ambos os casos, com as Comunicações - invocan<strong>do</strong> PLAY.req, PLAY.ind, etc...<br />

A.2: Primitivas de Comunicação<br />

Com. Primitiva .req por: Parâmetros<br />

Start START.req, START.ind Cliente MyName, Gf<br />

START.rsp, START.cnf Gf, Resulta<strong>do</strong>: Ok, GId, YourSymbol<br />

Ou Gf, Sorry + Reason]<br />

Join JOIN.req, JOIN.ind Cliente MyName, GId<br />

JOIN.rsp, JOIN.cnf GId, Resulta<strong>do</strong>: Ok, YourSymbol: X<br />

Ou GId, Sorry [+ Reason]<br />

Play PLAY.req, PLAY.ind Cliente GId, Line, Column<br />

PLAY.rsp, PLAY.cnf GId, Resulta<strong>do</strong>: Ok<br />

Ou GId, Sorry [+ Reason]<br />

BOARD.req, BOARD.ind Servi<strong>do</strong>r GId, Posição <strong>do</strong> <strong>Jogo</strong>;<br />

List L<strong>IST</strong>.req, L<strong>IST</strong>.ind<br />

(para os <strong>do</strong>is joga<strong>do</strong>res) Continue ou End + Winner: Name ou None<br />

Cliente<br />

GAMEL<strong>IST</strong>.req, GAMEL<strong>IST</strong>.ind Servi<strong>do</strong>r None ou GId/Name(s) [,…]<br />

Abort ABORT.req, ABORT.ind Cliente GId [,…]<br />

ABORT.req, ABORT.ind Servi<strong>do</strong>r<br />

(para os <strong>do</strong>is joga<strong>do</strong>res)<br />

GId<br />

ERROR.req, ERROR.ind Servi<strong>do</strong>r GId, Reason<br />

A.3 - Diagramas Temporais<br />

Start:<br />

Start é executa<strong>do</strong> sobre TCP. Existem <strong>do</strong>is cenários possíveis para a sua execução:<br />

1. já existe uma conexão-TCP estabelecida entre o Cliente e o Servi<strong>do</strong>r.<br />

Para tal cenário, apresentam-se <strong>do</strong>is diagramas: em to<strong>do</strong>s, começa-se por executar, no Cliente, START.req;<br />

ela é "ecoada" no Servi<strong>do</strong>r como START.ind.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 25 <strong>of</strong> <strong>39</strong><br />

JdG-rVIf: existe sucesso em Start: O Servi<strong>do</strong>r responde com START.rsp(Ok), que é "ecoada" no Cliente<br />

como START.cnf(Ok).<br />

JdG-rVIb: não existe sucesso em Start: No entrementes, ocorreu um crash <strong>do</strong> Servi<strong>do</strong>r, pelo que o Cliente,<br />

ao detectá-lo, gera localmente Sorry.<br />

2. ainda não existe nenhuma conexão entre o Cliente e o Servi<strong>do</strong>r.<br />

Para tal cenário, apresentam-se <strong>do</strong>is diagramas; em ambos, começa-se por estabelecer uma conexão-TCP<br />

entre o Cliente e o Servi<strong>do</strong>r.<br />

JdG-rVIc: o accept é devolvi<strong>do</strong> e recebi<strong>do</strong> - e executa-se START.req…<br />

JdG-rVId: o Servi<strong>do</strong>r não está liga<strong>do</strong>… pelo que não chega qualquer accept; este cenário pode ser detecta<strong>do</strong><br />

com um relógio, arma<strong>do</strong> aquan<strong>do</strong> da leitura <strong>do</strong> Start: ao fim de um certo perío<strong>do</strong> de tempo, ele expira - e pode<br />

dar-se a (má) notícia ao sócio…<br />

(Bem entendi<strong>do</strong>, se o accept chegar, aquele relógio é cancela<strong>do</strong>)<br />

Join:<br />

Join é também executa<strong>do</strong> sobre TCP. Os respectivos Diagramas acabam por ser análogos aos de Start.<br />

Play:<br />

Play é também executa<strong>do</strong> sobre TCP - e somente pode ser executa<strong>do</strong> sobre um <strong>Jogo</strong> já lança<strong>do</strong> com Start +<br />

Join. Deve ser pacífico traçar os respectivos Diagramas…


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 26 <strong>of</strong> <strong>39</strong><br />

JdG-rVIg: O Diagrama representa o começo de um jogo entre <strong>do</strong>is joga<strong>do</strong>res: um deles invoca Start, o outro<br />

invoca Join. O último faz então uma jogada (Play) - após o que o tabuleiro é divulga<strong>do</strong> a ambos; então, o primeiro<br />

digita, também ele, a sua jogada (Play)… Adiante-se que se utiliza extBoard para sinalizar uma decisão (o envio<br />

<strong>do</strong> Tabuleiro) desencadeada por um evento externo ao par Client/Servi<strong>do</strong>r afecta<strong>do</strong> por essa decisão<br />

List:<br />

List é executa<strong>do</strong> sobre UDP.<br />

JdG-rVIe: existe sucesso em List.<br />

JdG-rVIj: existe insucesso em List. O Servi<strong>do</strong>r ainda não foi activa<strong>do</strong>, pelo que o Cliente não aprsenta<br />

qualquer listagem.<br />

Abort:<br />

Abort é também executa<strong>do</strong> sobre TCP. Os Diagramas que se seguem contemplam alguns cenários possíveis.<br />

Adiante-se que neles se utiliza extAbort para sinalizar uma decisão <strong>do</strong> Servi<strong>do</strong>r (nomeadamente, o envio de<br />

sAbort-Pdu) desencadeada por um evento externo ao par Client/Servi<strong>do</strong>r afecta<strong>do</strong> por essa decisão.<br />

JdG-rVIIIa: Um <strong>do</strong>s joga<strong>do</strong>res, quiçá por achar que houve empate (ou porque está em má posição e não gosta<br />

de perder…) invoca Abort. O Servi<strong>do</strong>r sinaliza-o ao outro joga<strong>do</strong>r.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 27 <strong>of</strong> <strong>39</strong><br />

JdG-rVIIIb: Algum "brincalhão", que não um <strong>do</strong>s fulanos jogan<strong>do</strong>, faz-lhes uma partida de Carnaval: invoca<br />

Abort. Na prática, o Servi<strong>do</strong>r recebe uma cAbort-Pdu, e sinaliza-o aos Clientes desses fulanos.<br />

JdG-rVIIIc: No decorrer de um jogo, expira o Timeout após a última PDU recebida no Servi<strong>do</strong>r; este sinaliza-o<br />

aos Clientes <strong>do</strong>s <strong>do</strong>is joga<strong>do</strong>res.<br />

JdG-rVIIId: Alguém, que não um <strong>do</strong>s joga<strong>do</strong>res, invoca Abort - mas sem sucesso (por ex., porque o <strong>Jogo</strong><br />

referi<strong>do</strong> já havia termina<strong>do</strong>, entretanto). O Servi<strong>do</strong>r sinaliza-lhe isso mesmo.<br />

JdG-rVIIIe: O Servi<strong>do</strong>r s<strong>of</strong>re um crash. Os Clientes (já com conexões estabelecidas com o Servi<strong>do</strong>r), ao<br />

detectá-lo, geram localmente a Indicação de Aborto <strong>do</strong> Jog<br />

Eventos "de cima"<br />

Eventos "de baixo"<br />

Chegada de PDUs<br />

A.4a: Lista de Eventos<br />

Cliente Servi<strong>do</strong>r<br />

Coman<strong>do</strong>s <strong>do</strong> Joga<strong>do</strong>r:<br />

Start, Join, Abort, Play, List<br />

sStart-Pdu, sJoin-Pdu<br />

sPlay-Pdu, sBoard-Pdu<br />

sGameList-Pdu<br />

sAbort-Pdu, sError-Pdu<br />

Tomada de Decisões:<br />

Ok ou Sorry+Reason<br />

Board, GameList, Error<br />

cStart-Pdu, cJoin-Pdu,<br />

cPlay-Pdu, cList-Pdu,<br />

cAbort-Pdu<br />

Sinais oriun<strong>do</strong>s <strong>do</strong> TCP accept, close connect, close<br />

Eventos "internos" Iniciação <strong>do</strong> Cliente Iniciação <strong>do</strong> Servi<strong>do</strong>r<br />

Relógio TConnect<br />

Relógio TTTL<br />

End, SIGPIPE<br />

End, SIGPIPE


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 28 <strong>of</strong> <strong>39</strong><br />

JdG- rXc e JdG-rXd: o Boss no Cliente<br />

JdG-rXb: o Gestor de um <strong>Jogo</strong> no Cliente<br />

A.4b: Diagramas de Esta<strong>do</strong> <strong>do</strong> Cliente<br />

Variáveis de Esta<strong>do</strong>:<br />

- Gc: contabiliza o número de <strong>Jogo</strong>s que o Cliente tem em curso (pode ter até 4);<br />

- uma Tabela de Descritores, Gs[4], <strong>do</strong>s <strong>Jogo</strong>s em curso (até um máximo de 4); memoriza, por cada um,<br />

- Role, inicia<strong>do</strong> a -1, que conterá S=0 ou J=1 consoante tenha si<strong>do</strong> executa<strong>do</strong> Start ou Join;<br />

- Name, que será o argumento MyName associa<strong>do</strong> a Start ou Join;<br />

- o GId (GameId) atribuí<strong>do</strong> pelo Servi<strong>do</strong>r;<br />

- o Símbolo local (X ou O) atribuí<strong>do</strong> pelo Servi<strong>do</strong>r;<br />

- MyTurn, que será TRUE se e somente se a próxima Jogada couber ao joga<strong>do</strong>r local.<br />

- State, que regista o Esta<strong>do</strong> actual <strong>do</strong> <strong>Jogo</strong>.<br />

- uma Lista, AList, conten<strong>do</strong> os GIds associa<strong>do</strong>s a coman<strong>do</strong>s Abort;


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 29 <strong>of</strong> <strong>39</strong><br />

- ListReqed: será TRUE se e somente se foi enviada a cList-Pdu;<br />

Inicialmente, há que invocar-se cInit, que deverá fixar Gc=0, ListReqed=FALSE, e iniciar AList e Gs.<br />

Predica<strong>do</strong>s:<br />

- p10, valor lógico da afirmação "Gc é igual a 4";<br />

- p11, valor lógico de "AList não está vazia";<br />

- p12, valor lógico de "um GId assinala<strong>do</strong> num Abort ou sAbort-Pdu é local";<br />

- p13, valor lógico de "GId assinala<strong>do</strong> em Play ou s-Pdu não está em curso";<br />

- p14, valor lógico de "Line assinalada em Play inválida";<br />

- p15, valor lógico de "Column assinalada em Play inválida ";<br />

- p17, valor lógico de "após o cicloA,existem algum Gs{} com Role≠-1";<br />

- end, valor lógico de "a jogada termina o <strong>Jogo</strong>";<br />

- p19, valor lógico de "o GId assinala<strong>do</strong> num Join não é váli<strong>do</strong>";<br />

- p21, valor lógico de "MyName assinala<strong>do</strong> num Join não é váli<strong>do</strong>";<br />

- p22, valor lógico de "Gf assinala<strong>do</strong> em sStart-Pdu não está em curso";<br />

- p38, valor lógico de "não há <strong>Jogo</strong>s em curso sobre esta conexão";<br />

JdG-rIXd e JdG-rIXe: o Boss no Servi<strong>do</strong>r<br />

JdG-rIXc: o Gestor de um <strong>Jogo</strong> no Servi<strong>do</strong>r<br />

A.4c: Diagramas de Esta<strong>do</strong> <strong>do</strong> Servi<strong>do</strong>r<br />

Variáveis de Esta<strong>do</strong>:<br />

- Gc: contabiliza o número de <strong>Jogo</strong>s que o Servi<strong>do</strong>r tem em curso (pode ter até 4);


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 30 <strong>of</strong> <strong>39</strong><br />

- uma Tabela de Descritores, All[4], <strong>do</strong>s <strong>Jogo</strong>s em curso (até um máximo de 4); memoriza, por cada um,<br />

- Names [S..J] que serão os argumentos MyName associa<strong>do</strong>s a Start e Join,<br />

no que segue abrevia<strong>do</strong>s para SName e JName, respectivamente;<br />

- Sockets [SSock.. JSock] que serão os identifica<strong>do</strong>res das conexões por onde chegaram cStart e cJoin;<br />

- STurn, que será SSock ou JSock consoante a próxima Jogada couber ao emissor <strong>do</strong> Start ou ao <strong>do</strong> Join.<br />

- Board, a posição actual <strong>do</strong> Tabuleiro.<br />

- uma Lista, SockGIds, por cada Conexão estabelecida, referin<strong>do</strong> os GIds que a utilizam;<br />

Inicialmente, deve invocar-se o procedimento sInit, que deverá fixar Gc=0.<br />

Predica<strong>do</strong>s:<br />

- p10, valor lógico da afirmação "Gc é igual a 4";<br />

- p31, valor lógico de "o GId assinala<strong>do</strong> num cJoin-Pdu não é váli<strong>do</strong>";<br />

- p32, valor lógico de "o GId assinala<strong>do</strong> num cPlay-Pdu não é váli<strong>do</strong>";<br />

- p33, valor lógico de "Line assinalada em cPlay-Pdu inválida";<br />

- p34, valor lógico de "Column assinalada em cPlay-Pdu inválida ";<br />

- p35, valor lógico de "não é a vez deste joga<strong>do</strong>r" ou seja: "Sock ≠ STurn" ;<br />

- p36, valor lógico de "o GId assinala<strong>do</strong> em cAbort-Pdu não está em curso";<br />

- p37, valor lógico de "a conexão para o Cliente deixou de ter <strong>Jogo</strong>s em curso";<br />

- p38, valor lógico de "não há <strong>Jogo</strong>s em curso sobre esta conexão";<br />

Boss no Cliente:<br />

A.4d: Tabelas de Esta<strong>do</strong> <strong>do</strong> Cliente<br />

closed WAccept cConnected<br />

Start Gc++,Tc,connect,-Start{Gf} Gc++,-Start{Gf} Gc++,-Start{Gf}<br />

Join Gc++,Tc,connect,-Join{Gf} Gc++,-Join{Gf} Gc++,-Join{Gf}<br />

Play Sorry Sorry p13:Sorry<br />

~p13:-Play{GId}<br />

Abort AList++{GId},Tc,connect AList++{GId} cAbort;<br />

p12:-Abort{GId}<br />

sStartPdu -p22:-sStart{Gf}<br />

sJoinPdu -p13:-sJoin{GId}<br />

sPlayPdu -p13:-sPlay{GId}<br />

sBoardPdu -p13:-sBoard{GId}<br />

sAbortPdu -p38:Abort{GId}<br />

sErrorPdu Sorry<br />

accept ~Tc;<br />

p11: ciclo{AList}:{cAbort};<br />

p17:ciclo{Gs}:-accept<br />

⇒cConnected<br />

-p17:close<br />

close<br />

⇒closed<br />

~Tc;Sorry;ciclo{Gs}:-Abort;<br />

⇒closed<br />

TConnect Sorry; ciclo{Gs}:-Abort; close;<br />

⇒closed<br />

Esta<strong>do</strong> <strong>do</strong> Boss: Qualquer<br />

List ListRqed=T,cListPdu<br />

Sorry;<br />

⇒closed


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 31 <strong>of</strong> <strong>39</strong><br />

Gestor dum <strong>Jogo</strong> {Gf/GId} no Cliente:<br />

closed wAccept<br />

accept Role==J: cJoin<br />

⇒wsJoin<br />

Role==S: cStart<br />

⇒wsStart<br />

Start Role{Gf}=S<br />

⇒wAccept<br />

Join Role{Gf}=J<br />

⇒wAccept<br />

Abort +End<br />

⇒closed<br />

wsStart wsJoin wPlay wsPlay wsBoard<br />

Play Sorry p33∨p34:Sorry<br />

~p33∧~p34:cPlay<br />

⇒wsPlay<br />

Sorry Sorry<br />

sStart Ok: MyTurn=F<br />

⇒wsBoard<br />

Sorry: +End<br />

⇒closed<br />

sJoin Ok:MyTurn=T<br />

⇒wPlay<br />

Sorry: +End<br />

⇒closed<br />

sPlay Ok:<br />

⇒wsBoard<br />

Sorry:<br />

⇒wPlay<br />

sBoard end:+End<br />

⇒closed<br />

MyTurn:MyTurn=F<br />

⇒wsBoard<br />

~MyTurn:MyTurn=T<br />

⇒wPlay<br />

Abort +End<br />

+End<br />

+End<br />

+End +End<br />

⇒closed ⇒closed ⇒closed ⇒closed ⇒closed<br />

A.4e: Tabelas de Esta<strong>do</strong> <strong>do</strong> Servi<strong>do</strong>r<br />

Adiante, apresentam-se várias Tabelas. Adiante-se que, na coluna à esquerda, as PDUs (e close) vêem<br />

acompanhadas de uma referência à conexão por onde chegaram ao Servi<strong>do</strong>r).<br />

Boss no Servi<strong>do</strong>r:<br />

wcStart sConnected<br />

cStart<br />

p10:sStart{SSock,Gf,Sorry,Reason}<br />

{SSock}<br />

~p10: Gc++,-cStart{GId,SSock}<br />

cJoin<br />

{JSock}<br />

p31:sJoin{JSock,GId,Sorry,Reason}<br />

~p31:-cJoin{GId,JSock}<br />

cPlay<br />

p32:sPlay{Sock,GId,Sorry,Reason}<br />

{Sock}<br />

~p32: -cPlay {Sock,GId}<br />

cList sGameList {GameList} sGameList{GameList}<br />

connect Accept


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 32 <strong>of</strong> <strong>39</strong><br />

close<br />

{Sock}<br />

cAbort<br />

{Sock}<br />

Gestor dum <strong>Jogo</strong> {GId} no Servi<strong>do</strong>r:<br />

⇒sConnected<br />

ciclo{SockGIds}: -Abort{Sock,GId}<br />

⇒wcStart<br />

ciclo{GIds em cAbort}:<br />

p36:sError{Sock,Reason,GId}<br />

~p36: -Abort{Sock,GId}<br />

wcStart wcJoin wcPlay<br />

-cStart<br />

{SSock}<br />

sStart{SSock,Ok},TTTL ⇒wcJoin<br />

-cJoin<br />

{JSock}<br />

sJoin{JSock,Ok},STurn=J Sock,TTTL ⇒wcPlay<br />

-cPlay<br />

sPlay{Sock,Sorry} Sock≠STurn: sPlay{Sock,GId,Sorry}<br />

{Sock}<br />

Sock==STurn:<br />

end: ~TTTL, sPlay{Sock,GId,Ok},<br />

sBoard{SSock..JSock,GId,end}, +End<br />

⇒wcStart<br />

~end: sPlay{Sock,GId,Ok},<br />

sBoard{SSock..JSock,GId,~end},<br />

STurn =~STurn,TTTL<br />

TTTL sAbort,+End {GId}<br />

sAbort{SSock..JSock,GId}, +End<br />

⇒wcStart<br />

⇒wcStart<br />

-Abort<br />

{Sock}<br />

~SSock: ~TTTL, sAbort{SSock}, +End<br />

⇒wcStart<br />

~TTTL; +End,<br />

~SSock: sAbort{SSock,GId} ~JSock: sAbort{JSock,GId}<br />

⇒wcStart<br />

A5: Estrutura das PDUs<br />

PDU Cód Parâmetros PDU Cód Parâmetros<br />

cStart 1 Gf MyName<br />

sStart 2 Gf GId Symbol<br />

cJoin 3 GId MyName<br />

cAbort 5 GId GId…<br />

cPlay 8 GId Line Column<br />

sJoin<br />

2 Gf 0 Reason<br />

4 GId 1 Symbol<br />

4 GId 0 Reason<br />

sAbort 6 GId Reason<br />

sError 7 Reason GId GId…<br />

sPlay 9 GId Sorry Reason<br />

9 GId Ok<br />

sBoard 10 GId Board<br />

cList 11 sGameList 12 GameList


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 33 <strong>of</strong> <strong>39</strong><br />

Anexo B: Codificação <strong>do</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong><br />

Convirá recapitular a meto<strong>do</strong>logia percorrida a propósito <strong>do</strong> desenho <strong>do</strong> "<strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong>":<br />

- num primeiro passo, abor<strong>do</strong>u-se a sua Arquitectura de Comunicações…<br />

e, logo de seguida, a Arquitectura Modular <strong>do</strong> Elemento de Utiliza<strong>do</strong>r;<br />

- o passo seguinte centrou-se num elenco das Primitivas de Comunicação…<br />

e no desenho <strong>do</strong>s respectivos Diagramas Temporais - apresentan<strong>do</strong> as situações normais, e outras não tão<br />

normais assim: crashs, etc..<br />

- a partir daí, "deduziu-se" um conjunto de Diagramas Temporais…<br />

e, deles, chegou-se enfim às Tabelas de Esta<strong>do</strong>.<br />

Quan<strong>do</strong> - e somente quan<strong>do</strong>! - tais Tabelas estiverem especificadas, será hora de passar à codificação <strong>do</strong><br />

protocolo nalguma linguagem de programação! Atalhos eventualmente toma<strong>do</strong>s (como sejam a codificação antes<br />

da especificação) são sinónimo de desperdício de tempo, mormente na fase de debug/teste…<br />

A codificação faz-se a partir das Tabelas de Esta<strong>do</strong>! Adiante, e para não dispersar a atenção, a discussão ir-se-á<br />

centrar na Tabela <strong>do</strong> Gestor dum <strong>Jogo</strong> {GId} no Servi<strong>do</strong>r (no Anexo A.4e); fica todavia subentendi<strong>do</strong>: para<br />

completar o desenho de toda a Aplicação, é preciso, uma a uma, atender a todas as Tabelas a que se chegou!<br />

B.1: Acções Elementares<br />

Um relance de olhos àquela Tabela de Esta<strong>do</strong>s permite fazer um elenco das assim designadas Acções<br />

elementares: aparecem no seio das quadrículas, e são assim chamadas por não serem redutíveis a outras mais<br />

elementares. São elas:<br />

Acção Elementar Significa<strong>do</strong> Parâmetros<br />

sStart Preencher e enviar a sStart-PDU Socket, Gf, Resulta<strong>do</strong> [+Reason ou GId+Symbol]<br />

sJoin Preencher e enviar a sJoin-PDU Socket, GId, Resulta<strong>do</strong> [+Reason ou Symbol]<br />

sPlay Preencher e enviar a sPlay-PDU Socket, GId, Resulta<strong>do</strong> [+Reason]<br />

sBoard Preencher e enviar a sBoard-PDU Socket, GId, Board [+end]<br />

sAbort Preencher e enviar a sAbort-PDU Socket, GId, Reason<br />

TTTL Armar o relogio TimeToLive GId<br />

~TTTL Cancelar o relogio TimeToLive GId<br />

+End Sinalizar ao Boss o termo <strong>do</strong> <strong>Jogo</strong><br />

Uma primeira etapa na codificação consiste, precisamente, em estabelecer, par i passu, um elenco de<br />

procedimentos elementares, um para cada uma das supracitadas Acções Elementares. No caso, e na suposição de se<br />

utilizar a linguagem-C, ele poderá resolver-se nos procedimentos:<br />

int sStart ( Socket, Gf, Result, Reason, GId, Symbol ) ;<br />

int sJoin ( Socket, GId, Result, Reason, Symbol ) ;<br />

int sPlay ( Socket, Resulta<strong>do</strong> , Reason ) ;<br />

int sBoard ( Socket, Board ) ;<br />

int sAbort ( Socket, GId, Reason) ;<br />

int FireTTL ( GId ) ;<br />

int CancelTTL ( GId ) ;<br />

Um ponto obrigatório nesta etapa - porquanto há que codificar sStart, sJoin, etc. - é o estabelecimento definitivo<br />

da estrutura das PDUs - se é que isso ainda não foi feito. Um outro ponto é escrever aqueles procedimentos<br />

elementares recorren<strong>do</strong>:<br />

- às primitivas <strong>of</strong>erecidas pelo nível inferior (no caso, sendto e write)…<br />

- e (nomeadamente nos casos que envolvem a gestão <strong>do</strong> relógio) às chamadas ao sistema operativo específicas<br />

ao computa<strong>do</strong>r em que se há-de executar o protocolo.<br />

B.2: Acções Atómicas:<br />

Codificadas as acções elementares, é altura de considerar aquilo que se poderá designar de Acções Atómicas:<br />

acções que preenchem cada uma das quadrículas da Tabela em causa, e assim chamadas por não deverem nunca


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 34 <strong>of</strong> <strong>39</strong><br />

ser interrompidas. Pressuposto que no esta<strong>do</strong>-S acontece o evento-E, as correspondentes acções a tomar devem ser<br />

executadas e o esta<strong>do</strong>-seguinte assumi<strong>do</strong> - sem quebra <strong>do</strong> fluxo de controlo <strong>do</strong> programa: somente depois será hora<br />

de atender a algum outro evento.<br />

Na prática, ir-se-ão constituir tantas Acções Atómicas quantas as quadrículas da Tabela de Esta<strong>do</strong>s. Ei-las,<br />

deven<strong>do</strong> o leitor reparar no nome escolhi<strong>do</strong> para as designar: sempre o Nome-dum-Evento e o Nome-dum-Esta<strong>do</strong> -<br />

uni<strong>do</strong>s pela partícula Over.<br />

wcStart wcJoin wcPlay<br />

-cStart cStartOverwcStart cStartOverwcJoin cStartOverwcPlay<br />

-cJoin cJoinOverwcStart cJoinOverwcJoin cJoinOverwcPlay<br />

-cPlay cPlayOverwcStart cPlayOverwcJoin cPlayOverwcPlay<br />

TTTL TTLOverwcStart TTLOverwcJoin TTLOverwcPlay<br />

-Abort AbortOverwcStart AbortOverwcJoin AbortOverwcPlay<br />

O passo seguinte intui-se: trata-se de, quadrícula a quadrícula, elaborar as Acções Atómicas a partir <strong>do</strong>s<br />

verdadeiros tijolos que são as Acções Elementares; chegar-se-á a 15 procedimentos:<br />

int cStartOverwcStart ( )<br />

{sStart (S Sock, Gf, Ok, NULL , GId, Symbol ) ; FireTTL ( GId ) ; State=wcJoin ; return (0)}<br />

int cJoinOverwcJoin ( )<br />

{sJoin (JSock, GId, Ok, Symbol ) ; STurn= JSock; FireTTL ( GId ) ; State=wcPlay ; return (0)}<br />

int AbortOverwcJoin ( )<br />

{if ( Sock != SSock) {<br />

sAbort (Sock, GId, Reason) ; CancelTTL ( GId ) ; State=wcStart ; return ( End ) ; }}<br />

etc.<br />

Por mor de simplificação, foram omiti<strong>do</strong>s alguns detalhes, nomeadamente os Argumentos <strong>do</strong>s procedimentos.<br />

Entretanto, repare o leitor: a terminar cada um deles, e quan<strong>do</strong> existe mudança de Esta<strong>do</strong> para um outro Esta<strong>do</strong>,<br />

ocorre uma instrução da forma genérica State=NextState.<br />

B.3: O Núcleo <strong>do</strong> Protocolo<br />

Codificadas as Acções <strong>do</strong> protocolo, é hora de abordar o núcleo (kernel). Será pacífico entendê-lo como um<br />

ciclo "eterno"… Por cada iteração,<br />

- começa-se por aguardar o próximo evento;<br />

- aconteci<strong>do</strong> algum evento, determina-se a Acção Atómica a tomar quan<strong>do</strong> acontece o evento sucedi<strong>do</strong>;<br />

- invoca-se essa Acção;<br />

- no final, volta-se a aguardar o próximo evento…<br />

Em linguagem-C, ter-se-ia algo <strong>do</strong> género:<br />

gKeepGoing = TRUE ;<br />

<strong>do</strong> { /* Ciclo eterno, até gKeepGoing==FALSE */<br />

ActiveSocketCount = WaitNextEvent ( ) ; /* Aguarda o proximo evento */<br />

Action = GetHandler ( ActiveSocketCount , & Off ) ; /* Determina a Acção a executar */<br />

if ( Action ) RetVal = Action ( & Off ) ; /* Executa a Acção Atomica */<br />

} while ( gKeepGoing ) ;<br />

Adiante, apr<strong>of</strong>unda-se esta abordagem. Em particular, no Kernel constam <strong>do</strong>is novos procedimentos:<br />

WaitNextEvent e GetHandler. Como codificá-los?<br />

B.4: WaitNextEvent<br />

WaitNextEvent pode codificar-se de uma maneira assaz simples: mediante a invocação de uma chamada ao<br />

Sistema Operativo, select…


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 35 <strong>of</strong> <strong>39</strong><br />

ActiveSocketCount = select ( Width , & ReadFds , NULL , NULL , TimeOut ) ;<br />

Na prática, está-se requeren<strong>do</strong> ao Sistema Operativo que bloqueie o processo: ele fica suspenso até que ocorra<br />

um evento significativo - ou, mais formalmente: até que fique "ready" um "file-descriptor", no senti<strong>do</strong> de que se<br />

pode proceder a "read" nesse file-descriptor sem temer ficar bloquea<strong>do</strong>. O tempo de espera pode ser indefini<strong>do</strong> ou<br />

limita<strong>do</strong> por um TimeOut. A instrução após o select é executada somente após tal evento ocorrer. Tais filedescriptors<br />

podem ser os <strong>do</strong> tecla<strong>do</strong>, de sockets ou pipes.<br />

B.5: Handler de um Evento:<br />

Após WaitNextEvent, o Kernel invoca GetHandler. Considere-se, por exemplo, que o evento ocorri<strong>do</strong><br />

foi a chegada de uma PDU cStart; acontece que WaitNextEvent apenas informa que chegou mais uma PDU –<br />

porém sem discernir qual foi! Há, pois, que, primeiro que tu<strong>do</strong>, analisar essa PDU - para saber de que tipo é –, e<br />

somente depois se poderá invocar o pertinente Procedimento (no caso, cStartOver… )… Por outras palavras: as<br />

Acções Atómicas acima não podem ser invocadas directamente aquan<strong>do</strong> da ocorrência de um evento – há que fazêlo<br />

a partir dum procedimento genérico de tratamento desse evento, designemo-lo de Handler <strong>do</strong> evento, como seja:<br />

- OnUserCommand, Handler de um coman<strong>do</strong> <strong>do</strong> Utiliza<strong>do</strong>r (Start, Join, Abort, Play, List);<br />

- OnUDPData, Handler de uma PDU chegan<strong>do</strong> por um socket-UDP;<br />

- OnConnect, Handler de um connect (chegan<strong>do</strong> ao socket-listen <strong>do</strong> Servi<strong>do</strong>r);<br />

- OnAccept, Handler de um accept (chegan<strong>do</strong> ao socket <strong>do</strong> Cliente por onde se emitiu connect);<br />

- OnTCPData, Handler de uma PDU chegan<strong>do</strong> por um socket-TCP (cStart, sStart; etc.);<br />

- OnTimeout, Handler <strong>do</strong> expirar de um relógio.<br />

- OnSignal, Handler de um signal (SIGINT, SGALRM, SIGPIPE ou outro).<br />

Intui-se então que GetHandler devolve um destes Handlers – e que é ele quem vem a invocar a pertinente<br />

Acção Atómica.<br />

B.6: GetHandler<br />

GetHandler pode codificar-se de uma maneira assaz simples: mediante um Teste ao valor de retorno de<br />

select, ActiveSocketCount:<br />

- se for 0, isso significa tão somente que, desde que se invocou select, passou um tempo TimeOut - sem que<br />

nenhum <strong>do</strong>s file-descriptors tenha fica<strong>do</strong> "ready". Conclusão: GetHandler deve devolver OnTimeout.<br />

- se for -1, e errno!= EINTR, então houve erro; mas, se errno=EINTR, então houve uma interrupção - de que o<br />

expirar <strong>do</strong> relógio (posto a correr com alarm) é exemplo. Conclusão: GetHandler pode devolver NULL - ou<br />

algo mais preciso, com a designação genérica OnErr ou OnEintr…<br />

- se 1≤ ActiveSocketCount, e estiver On o bit em ReadFds correspondente ao file-descriptor <strong>do</strong> tecla<strong>do</strong>,<br />

isso significa que alguém premiu o tecla<strong>do</strong>. Conclusão: GetHandler deve devolver OnUserCommand.<br />

- se 1≤ ActiveSocketCount, e estiver On o bit em ReadFds correspondente ao file-descriptor de algum<br />

socket, isso significa que houve acitividade nesse socket: chegou algum Segmento da Internet (ou o socket ficou<br />

closed). Conclusão: GetHandler deve devolver, consoante o caso: OnUDPData, OnConnect ou OnTCPData…<br />

- e devolver, em Off, o socket em que houve actividade.<br />

B.7: Associação de Acções Genéricas a Sockets<br />

O procedimento select executa-se sobre uma máscara, seja MasterMask, de tamanho Width. Para que as<br />

coisas aconteçam como se disse, é necessário – antes da invocação de Kernel:<br />

- um procedimento, seja InitMasterMask, de inicialização dessa máscara;<br />

- a serem usadas comunicações por UDP, um procedimento, seja StartNetAccess(SOCK_DGRAM), de<br />

inicialização das comunicações-UDP – que devolva um UDPSocket para tais comunicações;<br />

- a serem usadas comunicações por TCP, um procedimento, seja StartNetAccess(SOCK_STREAM), de<br />

inicialização das comunicações-TCP – que devolva um ListenSocket para servir eventuais connects;<br />

- por cada posição na máscara a usar, uma instrução, seja AddToMasterMask, que a associe a uma das Acções<br />

de inicialização supracitadas; eis exemplos:


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 36 <strong>of</strong> <strong>39</strong><br />

AddToMasterMask ( gKeyboard , OnUserCommand , … ) ;<br />

AddToMasterMask ( UDPSocket , OnUDPData , … ) ;<br />

AddToMasterMask ( ListenSocket , OnConnect , … ) ;<br />

em que gKeyboard = fileno ( stdin ) ;<br />

Repare-se que AddToMasterMask pode vir a ser invocada mais tarde, nomeadamente aquan<strong>do</strong> da chegada de<br />

um connect - a que o Servi<strong>do</strong>r respondeu com accept:<br />

AddToMasterMask ( DataSocket , OnTCPData , … ) ;<br />

Com estes cuida<strong>do</strong>s, a chegada de, por exemplo, uma PDU cStart desbloqueia WaitNextEvent; a<br />

subsequente invocação de GetHandler devolve OnTCPData (e o socket Off em que ela foi recebida); intui-se que<br />

este deverá começar por invocar read sobre Off, analisar o tipo de PDU e, enfim, invocar a pertinente Acção<br />

Atómica (que deverá ser uma das seguintes: cStartOverwcStart, cStartOverwcJoin ou cStartOverwcPlay)…<br />

B.8: A Tabela de Acções Atomicas:<br />

Detecta<strong>do</strong> o tipo da PDU recebida, intui-se que a eleição da pertinente Acção atómica específica, como seja<br />

cStartOverwcJoin, envolve um fluxo assaz monótono:<br />

Determinação de TypeOfPDU (por análise da PDU recebida) ;<br />

if (TypeOfPDU == cStart ) {<br />

if ( State == wcStart )<br />

cStartOverwcStart ( ) ;<br />

else if ( State == wcJoin)<br />

cStartOverwcJoin ( ) ;<br />

else…<br />

}<br />

else if (TypeOfPDU == cJoin ) {<br />

}<br />

else if (TypeOfPDU == cPlay ) {<br />

}<br />

etc..<br />

Em vez da sequência if / else, pode-se utilizar uma sequência de switch / case, como seja:<br />

switch (TypeOfPDU) {<br />

case cStart :<br />

switch ( State ) {<br />

case wcStart :<br />

cStartOverwcStart ( ) ;<br />

break ;<br />

case wcJoin:<br />

cStartOverwcJoin ( ) ;<br />

break ;<br />

etc..<br />

São soluções pouco eficientes e flexíveis. Eis uma mais inteligente e elegante: defina-se um tipo, ActionType:<br />

typedef int ( * ActionType ) ( void * Parameter ) ;<br />

Na prática, o ActionType é um procedimento, com um parâmetro <strong>do</strong> tipo void*, que devolve um int.<br />

De seguida, define-se uma Tabela de acções atómicas, ActionsTable, com tantas linhas e colunas quantas as que<br />

a Tabela de Esta<strong>do</strong>s em causa tem; cada posição "linha/coluna" dessa ActionsTable será ocupada por um<br />

procedimento - precisamente aquele que codifica a acção atómica estabelecida na correspondente quadrícula<br />

"linha/coluna" daquela Tabela de Esta<strong>do</strong>s:<br />

ActionType ActionsTable [ 5 ] [ 3 ] = {<br />

cStartOverwcStart , cStartOverwcJoin , cStartOverwcPlay ,<br />

cJoinOverwcStart , cJoinOverwcJoin , cJoinOverwcPlay ,


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 37 <strong>of</strong> <strong>39</strong><br />

cPlayOverwcStart , cPlayOverwcJoin , cPlayOverwcPlay ,<br />

TTLOverwcStart , TTLOverwcJoin , TTLOverwcPlay ,<br />

AbortOverwcStart , AbortOverwcJoin , AbortOverwcPlay<br />

} ;<br />

Atribuem-se de seguida, a cada Esta<strong>do</strong> - wcStart, wcJoin e wcPlay - e Classe-de-Evento - cStart, cJoin, cPlay,<br />

TTL e Abort - considera<strong>do</strong>s, um valor inteiro entre 0 e, respectivamente, o número-de-colunas-menos-1 e o<br />

número-de-linhas-menos-1, inclusivé:<br />

# define wcStart 0 /* Identificação das colunas */<br />

# define wcJoin 1<br />

# define wcPlay 2<br />

# define cStart 0 /* Identificação das linhas */<br />

# define cJoin 1<br />

# define cPlay 2<br />

# define TTL 3<br />

# define Abort 4<br />

No caso em apreço (recepção de cStart), a Classe-de-Evento é 0; quanto ao Esta<strong>do</strong>, convirá registar a sua<br />

evolução num descritor associa<strong>do</strong> ao socket Off em causa - inicia<strong>do</strong> aquan<strong>do</strong> <strong>do</strong> connect/accept que activou esse<br />

socket.<br />

Conhecen<strong>do</strong>-se o State, então a Acção Atómica a invocar será ActionsTable [ Event ] [ State ] – e não mais é<br />

necessária a sequência if /else ou switch / case acima…<br />

B.9: Outras Inicializações:<br />

Antes de invocar o Kernel, quiçá se tenha que:<br />

- declarar o Handler <strong>do</strong> ctrl-C, com, por ex.: SetSignalCatcher ( SIGINT , OnSIGINT ) ;<br />

- declarar que se deve ignorar um eventual SIGPIPE, com: SetSignalCatcher ( SIGPIPE , SIG_IGN ) ;<br />

- declarar o Handler <strong>do</strong> relógio, com SetSignalCatcher ( SIGALRM , OnALRM ) ;<br />

(O leitor pode aceder a web.ist.utl.pt/~ist10898/public/<strong>Projecto</strong>s/CSources/index.htm, e proceder ao <strong>do</strong>wnload<br />

<strong>do</strong>s ficheiros Signals.c - que codifica SetSignalCatcher – e TCPAndUDPServer.c – que codifica um exemplo de<br />

OnSIGINT).<br />

Ademais, será de bom tom inicializar/limpar as estruturas de da<strong>do</strong>s pertinentes a cada socket…<br />

B.10: Relógios:<br />

Eventualmente, é preciso recorrer a múltiplos relógios – e todavia o computa<strong>do</strong>r encontra-se limita<strong>do</strong> a um<br />

único relógio físico. Como contornar esssa limitação?<br />

Uma solução é construir uma lista de descritores de relógios lança<strong>do</strong>s (mas ainda não-expira<strong>do</strong>s), ordena<strong>do</strong>s<br />

cronologicamente (pelo instante em que irão expirar) e acedida por quatro procedimentos:<br />

InitTimerModule, para iniciar aquela lista e registar (com SetSignalCatcher) o Handler <strong>do</strong> signal<br />

SIGALRM: ele deve ser trata<strong>do</strong> por OnTimeout;<br />

LaunchTimer, para se poder incluir nessa lista um novo descritor, indican<strong>do</strong>:<br />

- o instante em que deve expirar,<br />

- o Procedimento que deve ser invoca<strong>do</strong> se ele expirar,<br />

- e o eventual Argumento de que esse Procedimento necessitará.<br />

Eis um exemplo: TimerId = LaunchTimer ( cPeriod , OnTTL , … ) ;<br />

CancelTimer, para remover um descritor daquela lista<br />

GetRemainingTime, para indicar quanto tempo falta para o primeiro relógio expirar.<br />

Resta, apenas, utilizar uma de duas chamadas ao Sistema: select e alarm.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> 38 <strong>of</strong> <strong>39</strong><br />

- select deve ser invoca<strong>do</strong> quan<strong>do</strong> podem ser múltiplos os eventos exteriores a que o processo deve reagir, como<br />

seja a chegada de da<strong>do</strong>s (UDP ou TCP), pedi<strong>do</strong>s de conexão, o expirar de relógios e o premir <strong>do</strong> tecla<strong>do</strong>... Se não<br />

ocorrer nenhum <strong>do</strong>s eventos expectáveis durante o Perío<strong>do</strong> de tempo referi<strong>do</strong> no select, ele irá devolver 0 - e<br />

GetHandler irá devolver OnTimeout...<br />

- alarm pode ser invoca<strong>do</strong> quan<strong>do</strong>, de facto, um único evento é expectável. Eis algumas possibilidades:<br />

- a chegada dum Segmento (UDP ou TCP) a um Cliente que não espera por mais nada: antes de recvfrom ou<br />

read, executa LaunchTimer e alarm, e depois executa CancelTimer. Se não chegar nenhum segmento<br />

até ao Perío<strong>do</strong> de tempo em causa, o relógio irá expirar, e é executa<strong>do</strong> OnTimeout…<br />

- o expirar dum perío<strong>do</strong> máximo de pausa obrigatória: antes de pause, executa LaunchTimer e alarm. Ao<br />

fim de algum Perío<strong>do</strong> de tempo, o relógio irá expirar, e é executa<strong>do</strong> OnTimeout…<br />

OnTimeout deve varrer a lista de relógios e, para cada um que deveria ter expira<strong>do</strong> antes <strong>do</strong> momento actual,<br />

remove-o da lista e executa o Procedimento associa<strong>do</strong> (regista<strong>do</strong> aquan<strong>do</strong> <strong>do</strong> LaunchTimer que o criou)<br />

(O leitor pode aceder a web.ist.utl.pt/~ist10898/public/<strong>Projecto</strong>s/CSources/index.htm, e proceder ao <strong>do</strong>wnload<br />

de ficheiro Timers.c conforme ao que fica dito).<br />

B.11: Interrupções<br />

Por "atómica" que se reinvidique uma dada Acção, o que é facto é que ela demorará sempre um tempo maior ou<br />

menor a chegar ao fim: ela não é nunca instantânea! Por via disso, pode suceder que, enquanto ela se executa,<br />

venha a acontecer algum evento! Por exemplo: pode o relógio TTTL expirar quan<strong>do</strong> se está processan<strong>do</strong> a chegada<br />

de uma cPlay-Pdu (e aquele ainda não foi cancela<strong>do</strong>). Vice-versa, pode chegar uma cPlay-Pdu (que se atrasou)<br />

quan<strong>do</strong> se está já processan<strong>do</strong> o precedente expirar <strong>do</strong> relógio - mas, de facto, isto não é problemático: bastará<br />

invocar select só após o termo da ActionTable[] correspondente ao tratar <strong>do</strong> TimeOut.<br />

Com o fito de não quebrar o curso das Acções atómicas, uma solução é:<br />

- construir uma lista-<strong>do</strong>s-eventos-pendentes-de-análise;<br />

- sempre que ocorrer um evento no curso de execução de uma Acção atómica, suspendê-la por instantes, para<br />

invocar um Handler que registe esse evento naquela lista, e retomar de imediato a execução daquela Acção;<br />

- no fim, e antes de voltar a aguardar o próximo evento, em select, investigar primeiro se aquela lista está vazia;<br />

e, se o não estiver, proceder como se o evento que ela regista tivesse aconteci<strong>do</strong> precisamente agora.<br />

O Kernel ficaria então:<br />

gKeepGoing = TRUE ;<br />

<strong>do</strong> { /* Ciclo eterno, até gKeepGoing==FALSE */<br />

ActiveSocketCount = WaitNextEvent ( ) ; /* Aguarda o proximo evento */<br />

Action = GetHandler ( ActiveSocketCount , & Off ) ; /* Determina a Acção a executar */<br />

if ( Action ) RetVal = Action ( & Off ) ; /* Executa a Acção Atomica */<br />

while ( NotEmptyList ( PendingEventsList )) {<br />

PendingEvent = GetFirstOf (PendingEventsList ) ;<br />

PendingAction = GetPendingAction (PendingEvent ) ;<br />

RetVal = PendingAction (& Off ) ; /* Executa a Acção Atomica */<br />

} while ( gKeepGoing ) ;<br />

} while ( gKeepGoing ) ;<br />

B.12: Servin<strong>do</strong> Acções Longas a múltiplos Clientes…<br />

Pode a execução de uma Acção Atómica ser por demais demorada. No Servi<strong>do</strong>r, será algo problemático: como<br />

que um Cliente lhe cativa toda a atenção, enquanto os demais ficam "a ver navios". Pelo que, salvo indicação em<br />

contrário, um Servi<strong>do</strong>r deve servir os múltiplos Clientes em paralelo, isto é: gastan<strong>do</strong> um slot de tempo com um<br />

deles, depois um slot (com a mesma duração) com outro, etc., e depois voltar a gastar um slot com o primeiro, um<br />

slot com o segun<strong>do</strong> – e assim sucessivamente… Um continuum de ciclos em que por cada um gasta um slot com<br />

um Cliente - ao invés de acabar o trabalho requisita<strong>do</strong> pelo primeiro antes de passar ao segun<strong>do</strong>.


<strong>Pr<strong>of</strong></strong> V <strong>Vargas</strong>, <strong>IST</strong> <strong>Jogo</strong> <strong>do</strong> <strong>Galo</strong> (<strong>Resolução</strong>) <strong>Page</strong> <strong>39</strong> <strong>of</strong> <strong>39</strong><br />

Em ordem a lográ-lo, uma praxis comum é desencadear (com fork), por cada Cliente, um processo que lhe seja<br />

dedica<strong>do</strong> – relegan<strong>do</strong> para o Sistema Operativo a repartição <strong>do</strong> tempo <strong>do</strong> CPU pelas várias Acções concorrentes.<br />

Mas existe uma alternativa – que é a de fazer confluir to<strong>do</strong>s os Clientes sobre um mesmo Processo – e fazê-lo<br />

repartir o seu tempo – de uma maneira igualitária - pelos vários Clientes. Isso significa que a execução de uma<br />

Acção Atómica – se perdurar tempo demasia<strong>do</strong>! – deve ser repartida por vários slots de tempo.<br />

Seja, por exemplo, o caso da transmissão de um ficheiro deveras grande. Em vez de o transmitir to<strong>do</strong> para um<br />

Cliente, e somente depois ir servir os demais, poder-se-á fazer o seguinte:<br />

- enviar uma parte dele;<br />

- invocar um procedimento, seja SetPendingAction ( Off , Action ), que registe que no socket Off há<br />

uma Action temporariamente suspensa à espera de vez para continuar a executar-se;<br />

- regressar a WaitNextEvent, todavia configuran<strong>do</strong> o Timeout <strong>do</strong> select de mo<strong>do</strong> a não exceder 1.<br />

Então, WaitNextEvent ficará bloquea<strong>do</strong> não mais que 1.<br />

Quan<strong>do</strong> WaitNextEvent se desbloquear – e, se for o caso de a razão para tal ter si<strong>do</strong> um evento significativo, se<br />

ter executar o pertinente Handler -, dever-se-á varrer to<strong>do</strong>s os sockets activos: se houver algum em que esteja<br />

regista<strong>do</strong> um Acção suspensa à espera de vez, dever-se-á conceder-lhe mais algum tempo de execução…<br />

- no final, regressar a WaitNextEvent – todavia configuran<strong>do</strong> novamente o Timeout <strong>do</strong> select de mo<strong>do</strong> a não<br />

exceder 1 (se ainda houver alguma Action temporariamente suspensa à espera de vez).<br />

(O leitor pode aceder a web.ist.utl.pt/~ist10898/public/<strong>Projecto</strong>s/CSources/index.htm, e proceder ao <strong>do</strong>wnload<br />

<strong>do</strong> ficheiro Kernel.c - que codifica alguns <strong>do</strong>s procedimentos acima).<br />

B.13: Comentários a terminar<br />

Nível Transparência: A Arquitectura de Comunicações inclui um "Nível Transparência". Em termos de<br />

programação, isso conduz à inserção de código que saiba lidar com o PDU-length - em particular, que invoque a<br />

pertinente ActionTable somente quan<strong>do</strong> toda a PDU tiver si<strong>do</strong> já recolhida para um buffer;<br />

PDUs inválidas: A Arquitectura Modular <strong>do</strong> Elemento de Utiliza<strong>do</strong>r inclui um "Filtro de PDUs"; ele deverá<br />

conferir a validade de uma PDU provinda de um socket, rejeitan<strong>do</strong>-a se for caso disso. Em termos de programação,<br />

bastará invocar esse Filtro imediatamente antes de invocar a ActionTable.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!