sharivan

Exercícios de lógica da programação [28-10-2015]

Nov 5th, 2015
198
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 1) Dada o seguinte código em C++:
  2.  
  3.         /*
  4.          *
  5.          * mysocket.h
  6.          *
  7.          */
  8.  
  9.         #include <mysocketapi.h> // Header que conterá a nossa API de sockets a ser definida logo adiante
  10.  
  11.         class Socket;
  12.  
  13.         typedef void (*TOnConnecting)(Socket *socket);
  14.         typedef void (*TOnConnected)(Socket *socket);
  15.         typedef void (*TOnReceiveData)(Socket *socket, const char* data, int len);
  16.         typedef void (*TOnDisconnect)(Socket *socket);
  17.         typedef void (*TOnError)(Socket *socket, SOCKET_ERROR errcode);
  18.        
  19.         class Socket {
  20.         private:
  21.             int sckt; // Handle para o socket
  22.             bool connected;
  23.             char *host;
  24.             int port;
  25.        
  26.             // Eventos
  27.             TOnConnecting OnConnecting;
  28.             TOnConnected OnConnected;
  29.             TOnReceiveData OnReceiveData;
  30.             TOnDisconnect OnDisconnect;
  31.             TOnError OnError;
  32.            
  33.             // Event handler: Redireciona cada evento recebido para seu ponteiro de método correspondente (caso esteja setado).
  34.             void OnSocketEvent(SOCKET_EVENT event) {
  35.                 switch (event) {
  36.                 case SCKT_EVENT_CONNECTING:
  37.                     if (OnConnecting != NULL)
  38.                         (*OnConnecting)(this);
  39.                     break;
  40.                 case SCKT_EVENT_CONNECTED:
  41.                     connected = true;
  42.                     if (OnConnected != NULL)
  43.                         (*OnConnected)(this);
  44.                     break;
  45.                 case SCKT_EVENT_RECEIVEDATA:
  46.                     if (OnReceiveData != NULL) {
  47.                         int len = getreceivedlen(sckt);
  48.                         char *data = malloc(len * sizeof(char));
  49.                         getreceiveddata(sckt, data, len);
  50.                         (*OnReceiveData)(this, data, len);
  51.                         free(data);
  52.                     }
  53.                     break;
  54.                 case SCKT_EVENT_DISCONNECT:
  55.                     connected = false;
  56.                     if (OnDisconnect != NULL)
  57.                         (*OnDisconnect)(this);
  58.                     break;
  59.                 case SCKT_EVENT_ERROR:
  60.                     if (OnError != NULL) {
  61.                         int lasterror = getlasterror(sckt);
  62.                         (*OnError)(this, lasterror);
  63.                     }
  64.                     break;
  65.                 }
  66.             }
  67.            
  68.             // Método estático usado como handle para a funçãos setsocketeventhandler.
  69.             // Quando ocorrer um evento no socket, este método apenas redireciona o evento para o método OnSocketEvent acima.
  70.             static void _OnSocketEvent(int sckt, SOCKET_EVENT event, void *ptr) {
  71.                 ((Socket *)ptr)->OnSocketEvent(event);
  72.             }
  73.         public:
  74.             // Construtor
  75.             Socket() {
  76.                 // Inicializa os eventos como NULL
  77.                 OnConnecting = NULL;
  78.                 OnConnected = NULL;
  79.                 OnReceiveData = NULL;
  80.                 OnDisconnect = NULL;
  81.                 OnError = NULL;
  82.                
  83.                 connected = false;
  84.                
  85.                 sckt = createsocket(); // Cria o socket
  86.                 if (sckt != 0) // Se ele foi criado com sucesso...
  87.                     setsocketeventhandler(sckt, (void *)this, &_OnSocketEvent); // seta o nosso event handler para ficarmos atentos de todos os eventos que ocorrerem no socket.
  88.             }
  89.            
  90.             // Destrutor
  91.             ~Socket() {
  92.                 closesocket(sckt); // Fecha o socket
  93.                 sckt = 0; // Seta o handle para o socket como zero para indicar que ele está fechado e que nenhuma outra operação poderá ser feita depois disso.
  94.             }
  95.            
  96.             // Connecta a um host:port específico
  97.             void Connect(const char* host, int port) {
  98.                 this->host = host;
  99.                 this->port = port;
  100.                 if (sckt != 0)
  101.                     connect(sckt, host, port);
  102.             }
  103.            
  104.             // Envia dados para uma conexão previamente aberta
  105.             void SendData(const char* data, int len) {
  106.                 if (sckt != 0)
  107.                     send(sckt, data, len);
  108.             }
  109.            
  110.             // Fecha a conexão previamente aberta
  111.             void Disconnect() {
  112.                 if (sckt != 0)
  113.                     disconnect(sckt);
  114.             }
  115.            
  116.             char *GetHost() {
  117.                 return host;
  118.             }
  119.            
  120.             int GetPort() {
  121.                 return port;
  122.             }
  123.            
  124.             bool IsConnected() {
  125.                 return connected;
  126.             }
  127.            
  128.             // propriedade OnConnecting
  129.             TOnConnecting GetOnConnecting() {
  130.                 return OnConnecting;
  131.             }
  132.            
  133.             void SetOnConnecting(TOnConnecting OnConnecting) {
  134.                 this->OnConnecting = OnConnecting;
  135.             }
  136.            
  137.             // propriedade OnConnected
  138.             TOnConnected GetOnConnected() {
  139.                 return OnConnected;
  140.             }
  141.            
  142.             void SetOnConnected(TOnConnected OnConnected) {
  143.                 this->OnConnected = OnConnected;
  144.             }
  145.            
  146.             // propriedade OnReceiveData
  147.             TOnReceiveData GetOnReceiveData() {
  148.                 return OnReceiveData;
  149.             }
  150.            
  151.             void SetOnReceiveData(TOnReceiveData OnReceiveData) {
  152.                 this->OnReceiveData = OnReceiveData;
  153.             }
  154.            
  155.             // propriedade OnDisconnect
  156.             TOnDisconnect GetOnDisconnect() {
  157.                 return OnDisconnect;
  158.             }
  159.            
  160.             void SetOnDisconnect(TOnDisconnect OnDisconnect) {
  161.                 this->OnDisconnect = OnDisconnect;
  162.             }
  163.            
  164.             // propriedade OnError
  165.             TOnError GetOnError() {
  166.                 return OnError;
  167.             }
  168.            
  169.             void SetOnError(TOnError OnError) {
  170.                 this->OnError = OnError;
  171.             }
  172.         }
  173.        
  174.     A classe Socket definida acima por sua vez encapsula a seguinte API destinada a comunicação assíncrona via TCP sockets:
  175.    
  176.         /*
  177.          *
  178.          * mysocketapi.h
  179.          *
  180.          */
  181.    
  182.         enum SOCKET_EVENT {
  183.             SCKT_EVENT_CONNECTING = 0,
  184.             SCKT_EVENT_CONNECTED = 1,
  185.             SCKT_EVENT_RECEIVEDATA = 2,
  186.             SCKT_EVENT_DISCONNECT = 3,
  187.             SCKT_EVENT_ERROR = 4
  188.         }
  189.        
  190.         enum SOCKET_ERROR {
  191.             SCKT_ERROR_NO_ERROR = 0,
  192.             SCKT_ERROR_CONNECT = 1,
  193.             SCKT_ERROR_SEND = 2,
  194.             SCKT_ERROR_DISCONNECT = 3,
  195.             SCKT_ERROR_CLOSE = 4
  196.         }
  197.    
  198.         typedef void (SocketEventHandler*)(int sckt, int event, void *ptr)
  199.    
  200.         /*
  201.          *
  202.          * Retorno: Se criado com sucesso, retorna o handle para o socket (diferente de zero). Em caso de erro retorna 0.
  203.          *
  204.          * Descrição: Cria um novo socket tcp e retorna seu número de identificação (handle)
  205.          *
  206.          */
  207.         int createsocket();
  208.        
  209.         /*
  210.          *
  211.          * Parâmetros:
  212.          *
  213.          *   socket: Handle para o socket
  214.          *   ptr: Um ponteiro para uma estrutura de dados qualquer ou NULL caso não deseje passar nenhum ponteiro. Este ponteiro será o mesmo que será passado no parâmetro ptr na chamada do callback do event handler.
  215.          *   handler: A função de callback usada para ser chamada assim que ocorrer um evento no socket.
  216.          *
  217.          * Descrição: Define um novo event handler para o socket
  218.          *
  219.          */
  220.         void setsocketeventhandler(int socket, const void *ptr, SocketEventHandler handler);
  221.        
  222.         /*
  223.          *
  224.          * Parâmetros:
  225.          *
  226.          *   socket: Handle para o socket
  227.          *
  228.          * Descrição:
  229.          *   Fecha o socket.
  230.          *   Se existir alguma conexão aberta no socket, ela também será fechada.
  231.          *   Qualquer event handler associado ao socket também será removido.
  232.          *   Se ocorrer um erro durante o fechamento da conexão, ele será passado como um evento SCKT_EVENT_ERROR com o código de erro SCKT_ERROR_CLOSE para o handler (caso ele tenha sido previamente definido com setsocketeventhandler).
  233.          *
  234.          */
  235.         void closesocket(int socket);
  236.        
  237.         /*
  238.          *
  239.          * Parâmetros:
  240.          *
  241.          *   socket: Handle para o socket
  242.          *   host: endereço do host de destino
  243.          *   port: porta do host de destino
  244.          *
  245.          * Descrição:
  246.          *   Abre uma conexão com o host de destino e atribui ela ao socket.
  247.          *   Se ocorrer um erro durante a conexão (por exemplo, se o host não responder ou se a conexão for encerrada por ele ativamente), ele será passado como um evento SCKT_EVENT_ERROR com o código de erro SCKT_ERROR_CONNECT para o handler (caso ele tenha sido previamente definido com setsocketeventhandler).
  248.          *
  249.          */
  250.         void connect(int socket, const char* host, int port);
  251.        
  252.         /*
  253.          *
  254.          * Parâmetros:
  255.          *
  256.          *   socket: Handle para o socket
  257.          *   data: buffer contendo os dados a serem enviados
  258.          *   len: quantidade de bytes contidos no buffer
  259.          *
  260.          * Descrição:
  261.          *   Envia uma cadeia de bytes atráves da conexão previamente aberta.
  262.          *   Se ocorrer um erro durante o envio dos dados (por exemplo, se a conexão for encerrada durante a transmissão ou se ela já estiver fechada), ele será passado como um evento SCKT_EVENT_ERROR com o código de erro SCKT_ERROR_SEND para o handler (caso ele tenha sido previamente definido com setsocketeventhandler).
  263.          *
  264.          */
  265.         void send(int socket, const char* data, int len);
  266.        
  267.         /*
  268.          *
  269.          * Retorno: Código de erro para o último erro ocorrido no socket.
  270.          *
  271.          * Descrição:
  272.          *   Retorna o código referente ao último erro ocorrido no socket.
  273.          *   Usualmente essa função deverá ser chamada quando realmente ocorrer um erro no socket.
  274.          *
  275.          */
  276.         SOCKET_ERROR getlasterror(int socket);
  277.        
  278.         /*
  279.          *
  280.          * Parâmetros:
  281.          *
  282.          *   socket: Handle para o socket
  283.          *
  284.          * Descrição:
  285.          *   Encerra a conexão previamente aberta no socket.
  286.          *   Se ocorrer um erro durante o envio dos dados (por exemplo, se o socket já estava desconectado), ele será passado como um evento SCKT_EVENT_ERROR com o código de erro SCKT_ERROR_DISCONNECT para o handler (caso ele tenha sido previamente definido com setsocketeventhandler).
  287.          *
  288.          */
  289.         void disconnect(int socket);
  290.    
  291.     Uma vez que as seguintes suposições sejam verdadeiras:
  292.    
  293.         1) A API de comunicação via TCP sockets exposta acima é assíncrona, ou seja, todo o processo de comunicação ocorre em uma linha de execução separada de nosso programa.
  294.         2) Nunca haverá problemas de sincronização entre todo o código aqui exposto e a API assíncrona exposta acima. Entre outras palavras, isso significa que não precisaremos nos preocupar com possíveis implementações de seções críticas em nosso programa para garantir a sincronização correta do fluxo de dados.
  295.         3) Tudo o que foi definido anteriormente faça exatamente o que foi especificado conforme está em suas descrições.
  296.         4) Exista um servidor na internet rodando sob host fictício fanzackviadinhodafaquinha.com.br aberto na porta 24 no qual a cada requisição enviada que termine com o caracter de quebra de linha '\n' ele sempre retorná uma string numérica representando o comprimento da sua última requisição recebida (também terminada com '\n') sem contar com o caractere de quebra de linha. Por exemplo, se for enviado para o servidor a string "HELLO\n" ele retornará a string "5\n".
  297.         5) Não ocorra nenhuma falha na comunicação entre o cliente e o servidor a não ser que ela seja provocada pelo próprio cliente. Entre outras palavras, a conexão entre os dois será supostamente estável.
  298.        
  299.     Qual deverá ser a saída do programa abaixo?
  300.    
  301.         /*
  302.          *
  303.          * programa.cpp
  304.          *
  305.          */
  306.    
  307.         #include <stdio.h>
  308.         #include <string.h>
  309.         #include <windows.h>
  310.         #include <mysocket.h> // Header que contém a nossa classe Socket
  311.        
  312.         char buf[65536]; // Buffer de entrada
  313.         int pos; // Posição para o buffer de entrada
  314.         char lastLine[256]; // Armazena a última linha lida
  315.        
  316.         char phrases[16][16] = { // Array contendo as frases associadas para cada número lido do servidor que deverá ser enviada ao servidor.
  317.             "HELLO",
  318.             "Oi?",
  319.             "Td bem??",
  320.             "Cansei, tchau!!",
  321.             "Tchau!!!!!!!!!!!",
  322.             ":/",
  323.             "Vai demorar?",
  324.             "Cade vc???",
  325.             "Acorda!",
  326.             ".",
  327.             "Reponde!!!!",
  328.             "Responde F**!",
  329.             "Como vai?",
  330.             "Aloooo",
  331.             "Blz?",
  332.             "A v** vc vai!!"
  333.         }
  334.        
  335.         /*
  336.          *
  337.          * Parâmetros:
  338.          *   buf: Buffer para a entrada de dados (uma string).
  339.          *   pos: Posição do buffer no qual se iniciará a leitura (passado por referência).
  340.          *   dest: Buffer de saída contendo os dados lidos.
  341.          *
  342.          * Retorno: true se foi encontrado o caractere de quebra de linha durante a leitura, falso caso contrário.
  343.          *
  344.          * Descrição: Lê uma linha que possivelmente esteja terminada com o caractere de quebra de linha '\n'.
  345.          *
  346.          */
  347.         bool ReadLine(const char* buf, int &pos, char *dest) {
  348.             int len = strlen(buf);
  349.             int counter = 0;
  350.             bool foundLineBreak = false;
  351.             for (; counter < len; counter++) {
  352.                 char c = buf[pos++];
  353.                 if (c == '\n') {
  354.                     foundLineBreak = true;
  355.                     counter++;
  356.                     break;
  357.                 }
  358.                 dest[counter] = c;
  359.             }
  360.            
  361.             dest[counter] = '\0'; // Nunca se esquecer que strings em C ou C++ são sempre terminadas com um caractere nulo.
  362.            
  363.             return foundLineBreak;
  364.         }
  365.    
  366.         void OnConnecting(Socket *socket) {
  367.             printf("Conectando...");
  368.         }
  369.        
  370.         void OnConnected(Socket *socket) {
  371.             printf("Conectado.");
  372.         }
  373.        
  374.         /*
  375.          *
  376.          * Descrição: Processa uma linha de dados atualmente lida do servidor que segundo a descrição dada deve ser sempre uma string numérica contendo o tamanho da última requisição enviada. Se o número lido estiver entre 1 e 15 (incluindo o 1 e o 15) uma nova requisição será enviada ao servidor de acordo com o número lido, caso contrário a conexão será fechada (e consequentemente o programa será encerrado).
  377.          *
  378.          */
  379.         void ProcessInput(Socket *socket, char *input) {
  380.             int number = atoi(input); // A função atoi converte uma string para um número inteiro
  381.             if (number < 1 || number > 15) {
  382.                 socket->disconnect();
  383.                 return;
  384.             }
  385.            
  386.             printf("Fanzack: %d", number);
  387.             char *phrase = &phrases[number];
  388.             printf("Eu: %s", phrase);
  389.             strcat(phrase, "\n");
  390.             socket.SendData(phrase, strlen(phrase));
  391.         }
  392.        
  393.         void OnReceiveData(Socket *socket, const char* data, int len) {
  394.             if (len <= 0)
  395.                 return;
  396.            
  397.             strcat(buf, data); // Lembrando que a funçao strcat concatena duas strings e o resultado será atribuido no primeiro parâmetro. Neste caso em particular seria o mesmo que fazer buf = buf + data (ou buf += data) em C# ou Java.
  398.             while (true) {
  399.                 char line[256];
  400.                 if (!ReadLine(buf, pos, line)) { // Tenta ler a próxima linha, se não for encontrado o caracter de quebra de linha no buffer a partir da posição informada então salva os dados atualmente lidos e sai da sub-rotina com a esperança de que na próxima lida o caracter de quebra de linha irá aparecer.
  401.                     strcpy(lastLine, line);
  402.                     return;
  403.                 }
  404.                 strcat(lastLine, line);
  405.                 stpcpy(line, lastLine); // stpcpy realiza uma cópia de uma string para outra onde a string de destino é o primeiro parâmetro. Note que fazer isso não é o mesmo que fazer line = lastLine pois esta última copia o pointeiro de lastLine para line enquanto strcpy copia os dados de lastLine para line.
  406.                 lastLine[0] = '\0';
  407.                 memmove(buf, buf + pos, pos); // Após ler a linha, realiza um deslocamento para a direita na nossa string de forma que os dados anteriormente lidos sejam apagados dela. Nada melhor do que a função memmove para esta operação. Poderia ser usada a função memccpy ao invés de memmove aqui?
  408.                 buf[pos] = '\0';
  409.                 pos = 0;
  410.                
  411.                 ProcessInput(socket, line); // Processa a linha lida.
  412.             }
  413.         }
  414.        
  415.         void OnDisconnect(Socket *socket) {
  416.             printf("Desconectado.");
  417.         }
  418.        
  419.         void OnError(Socket *socket, SOCKET_ERROR errcode) {
  420.             switch (errcode) {
  421.                 case SCKT_ERROR_CONNECT:
  422.                     printf("Erro durante a conexão.");
  423.                     break;
  424.                 case SCKT_ERROR_SEND:
  425.                     printf("Erro durante o envio de dados.");
  426.                     break;
  427.                 case SCKT_ERROR_DISCONNECT:
  428.                     printf("Erro durante a desconexão.");
  429.                     break;
  430.                 case SCKT_ERROR_CLOSE:
  431.                     printf("Erro durante o fechamento do socket.");
  432.                     break;
  433.                 default:
  434.                     printf("Erro desconhecido.");
  435.             }
  436.         }
  437.    
  438.         void main() {
  439.             buf[0] = '\0'; // Uma forma de inicializar uma string fixa como string nula é apenas setando sua primeira posição com o caracter nulo '\0'. Lembrando que nas linguagens C e C++ todas as strings são terminadas com o caractere nulo, é a forma usada por essas linguagens de delimitar a extensão de uma string.
  440.             pos = 0;
  441.             lastLine[0] = '\0';
  442.  
  443.             Socket socket;
  444.             socket.SetOnConnecting(&OnConnecting);
  445.             socket.SetOnConnected(&OnConnected);
  446.             socket.SetOnReceiveData(&OnReceiveData);
  447.             socket.SetOnDisconnect(&OnDisconnect);
  448.             socket.SetOnError(&OnError);
  449.             socket.Connect("fanzackviadinhodafaquinha.com.br", 24);
  450.             char *data = "HELLO\n";
  451.             printf("Eu: %s", data);
  452.             socket.SendData(data, strlen(data));
  453.             while(socket.IsConnected())
  454.                 Sleep(1000); // Espera 1 seguindo
  455.         }
RAW Paste Data