Apresentando o EXtensible Server Core (exsc). Parte 1

imagem



Olá pessoal! Quero compartilhar com o público a estrutura com base na qual existem atualmente muitos servidores atendendo a milhares de clientes em vários sistemas de servidor. exsc (EXtensible Server Core) é uma estrutura escrita em C e permite que você tenha um ou mais threads de servidor em um aplicativo. Cada thread de servidor é capaz de atender a um grande número de clientes. Embora a estrutura possa ser usada em um modelo de solicitação-resposta, ela foi projetada principalmente para manter uma conexão constante com um grande número de clientes e trocar mensagens em tempo real. Como eu mesmo gosto de pegar um projeto HelloWorld pronto, compilá-lo e ver como tudo funciona, no final do artigo postarei um link para esse projeto.



Muitas operações são feitas para uma conexão específica. Dentro da estrutura desta estrutura, a estrutura exsc_excon é responsável pela conexão. Essa estrutura possui os seguintes campos:



ix - índice de conexão. Este é o número de série da conexão que estava livre no momento em que o cliente se conectou.

id - identificador de conexão. Este é um número de conexão exclusivo. Ao contrário de um índice, ele não se repete.

addr -

nome do endereço IP do cliente - nome da conexão. Várias conexões podem ser nomeadas com o mesmo nome e, em seguida, algumas mensagens podem ser enviadas para todas as conexões com o mesmo nome (consulte a função exsc_sendbyname).



Inicialização do kernel



Para trabalhar com o kernel, precisamos inicializá-lo usando a função



void exsc_init(int maxsrvcnt);
      
      





O parâmetro maxsrvcnt informa ao kernel quantos threads de servidor usaremos em nosso aplicativo.



Iniciando o fluxo do servidor



Em seguida, precisamos iniciar o fluxo do servidor usando a função



int exsc_start(uint16_t port, int timeout, int timeframe, int recvbufsize, int concnt,
               void newcon(struct exsc_excon excon),
               void closecon(struct exsc_excon excon),
               void recv(struct exsc_excon excon, char *buf, int bufsize),
               void ext());
      
      





porta - a porta que ouvirá o fluxo do servidor.



tempo limite - indica quanto tempo o encadeamento do servidor aguardará por qualquer atividade do cliente. Se durante esse tempo o cliente não tiver enviado nenhuma mensagem, o encadeamento do servidor fecha essa conexão. Portanto, se quisermos manter uma conexão constante e definir este parâmetro por exemplo 30 segundos, é necessário enviar qualquer mensagem ping a cada 10-15 segundos.



prazo- o período de tempo para o qual permitimos que a solicitação seja executada. Por exemplo, se este valor for definido como 100 milissegundos e o thread do servidor processou todas as solicitações atuais dos usuários em 10 segundos, ele deixará os 90 milissegundos restantes para o processador para realizar outras tarefas. Portanto, quanto menor esse valor, mais rápido o thread do servidor processará as solicitações, mas mais carregará o processador.



recvbufsize - o tamanho do buffer que a thread do servidor irá ler por vez.



concnt - o número máximo de conexões com as quais o encadeamento do servidor funciona simultaneamente.



newcon- função de retorno de chamada que funcionará toda vez que um novo cliente se conectar. Os parâmetros desta função serão passados ​​na conexão do cliente que se conectou.



closecon é uma função de retorno de chamada que será executada sempre que uma conexão inativa for encerrada. Os parâmetros desta função serão passados ​​na conexão do cliente que se desconectou.



recv é uma função de retorno de chamada que será chamada quando o cliente enviar pacotes. Os parâmetros desta função serão passados ​​a conexão do cliente de onde vieram os dados, um ponteiro para os dados e o tamanho do buffer com os dados.



ext- função de retorno de chamada que será chamada a cada passagem do loop de thread do servidor. Esta função é feita para estender a funcionalidade do kernel. Por exemplo, você pode vincular o processamento de temporizadores aqui.



A função exsc_start retorna um identificador para o encadeamento do servidor, que será necessário para chamar algumas das funções do framework.



Envio de mensagens



A função é responsável pelo envio de mensagens.



void exsc_send(int des, struct exsc_excon *excon, char *buf, int bufsize);
      
      





Esta função é segura para thread (você pode chamá-la de qualquer thread). Como parâmetros, você precisa passar para ele o identificador do fluxo do servidor (que recebemos como o valor de retorno da função exsc_start ), a conexão para a qual queremos enviar uma mensagem, um ponteiro para o buffer com a mensagem e o tamanho do buffer.



Também temos a oportunidade de enviar uma mensagem a um grupo de clientes. Existe uma função para isso



void exsc_sendbyname(int des, char *conname, char *buf, int bufsize);
      
      





É semelhante à função exsc_send, exceto pelo segundo parâmetro, que é o nome das conexões para as quais a mensagem será enviada.



Configurando o nome da conexão



Para identificar melhor a conexão no futuro, ou armazenar algumas informações sobre ela com a conexão, ou enviar mensagens para um grupo de clientes, use a função



void exsc_setconname(int des, struct exsc_excon *excon, char *name);
      
      





Esta função é segura para thread. O primeiro parâmetro é o identificador do fluxo do servidor, o segundo parâmetro é a própria conexão e o terceiro parâmetro é o nome dessa conexão.



Conectando um stream de servidor a outro servidor



Às vezes, a lógica do lado do servidor exige que você se conecte a outro servidor para solicitar ou transmitir quaisquer dados. Para tais tarefas, foi introduzida uma função que cria tal conexão.



void exsc_connect(int des, const char *addr, uint16_t port, struct exsc_excon *excon);
      
      





Esta função é thread-safe. Como parâmetros, precisamos passar o identificador do stream do servidor, o endereço do servidor ao qual precisamos nos conectar, o consumo do servidor ao qual precisamos nos conectar e, como último parâmetro, passamos o ponteiro de conexão com o qual podemos mais tarde, chame outras funções da estrutura. É importante notar que não precisamos esperar que a conexão ocorra. Podemos chamar as funções exsc_connect e exsc_send uma após a outra e o sistema garantirá que a mensagem seja enviada assim que puder se conectar ao servidor remoto.



Servidor de exemplo com comentários



#include <stdio.h>
#include <string.h>
#include "../exnetwork/exsc.h"

int g_des; //   

//   
void exsc_newcon(struct exsc_excon con)
{
    printf("the connection was open  %s\n", con.addr);
}

//  
void exsc_closecon(struct exsc_excon con)
{
    printf("the connection was closed  %s\n", con.addr);
}

//    
void exsc_recv(struct exsc_excon con, char *buf, int bufsize)
{
    char msg[512] = { 0 };
    memcpy(msg, buf, bufsize);
    printf("receive data from %s\n%s\n", con.addr, msg);

    if (strcmp(msg, "say hello") == 0)
    {
        strcpy(msg, "hello");
        exsc_send(g_des, &con, msg, strlen(msg));
    }
}

//    
void exsc_ext()
{
}

int main()
{
    printf("server_test_0 is started\n");

    exsc_init(2); //        

    //      7777
    //      30 
    //       10 
    //      1024 
    //      10000
    g_des = exsc_start(7777, 30, 10, 1024, 10000, exsc_newcon, exsc_closecon, exsc_recv, exsc_ext);

    //   ,      
    //       exit    ENTER
    while (1)
    {
        const int cmdmaxlen = 256;
        char cmd[cmdmaxlen];
        fgets(cmd, cmdmaxlen, stdin);
        if (strcmp(cmd, "exit\n") == 0)
        {
            break;
        }
    }
    return 0;
}

      
      





Conclusão



O kernel exsc faz apenas um baixo nível de interação com os clientes. Embora este seja o elemento mais importante do sistema servidor, a base sobre a qual tudo é construído, além disso, você precisa construir níveis superiores que serão responsáveis ​​por gerenciar conexões, gerar mensagens, montar mensagens (que provavelmente virão várias etapas). Se este artigo tiver uma resposta positiva, uma segunda parte será escrita, que desenvolverá o tópico de desenvolvimento de um programa de servidor de nível superior baseado neste kernel.



PS

Se alguém decidir entender a lógica interna deste framework ou aplicá-la em seus projetos e encontrar bugs ou gargalos, por favor nos avise. A comunidade é para isso, para que todos que possam contribuir com projetos de código aberto.



Link para a biblioteca O



exemplo está localizado no arquivo exsc_test_0.zip



All Articles