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