// diretiva de compilação gcc arquivo_fonte.c -o arquivo_objeto -lpthread // thread -> compartilhamento de memória // uso pesado de memória // usar semáfotos ou mutexes para gerenciar // funcionamento: um processo cliente, um processo pai que gera varias threads_servidores. Cada thread // servidora atende um cliente // como rodar: ./servidor 3(nº de clientes) //rodar o cliente como: ./cliente 3 #include #include #include #include #include #include #include #include #include #include #include /*Organiza a fila de conexões*/ /*estrutura usada para armazenar os dados de um cliente*/ struct cliente{ char apelido[20]; int ativo;/*variável que identifica se o cliente está ativo = 1, ou inativo = 0 OBS: TALVEZ NÃO PRECISE*/ int sock;/*Obtem o descritor de um cliente*/ int master;/*Essa variável é usada para identificar se o usuário tem poder sobre os demais, sendo 1 ou 0*/ }typedef CLIENTES; void* thread_proc(void *arg); void inicial();/*Seta 0 no atributo ativo da estrutura CLIENTES*/ void adiciona_cliente(int sock);/*Adiciona um novo clinete no vetor*/ void envia_msg_para_todos(int sock, char* msg);/*Envia uma mensagem do cliente para os demais clientes, menos o próprio cliente que digitou*/ void remove_cliente(int sock);/*Remove o cliente e mostra aos usuários*/ void get_cmd(char *msgUser, char *cmd); /*Obtem o comando que o usuario digitou para a mensagem*/ CLIENTES cli[20]; int total = 0; int main(int argc, char *argv[]) { struct sockaddr_in servidor, cliente; fd_set readset, testset; int meu_socket, novo_socket; char buffer[25]; int ouvir_socket; int n_lidos; int resultado; int nfilhos=5; pthread_t thread_id; int x, valor; //Variáveis interessantes if(argc > 1){ nfilhos = atoi(argv[1]); } meu_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* SO_REUSEADDR -> durante a compilação é comum estartar e parar várias vezes o computador O linux tende a manter o endereço e a porta reservada para aplicação. Essa opção libera esses argumetnos. A função setsocketopt define as opções de argumento no nível de protocolo. O nível de argumento especifica o nível de protocolo onde as opções residem. Para especificar as opções no nivel socket usa-se o argumento SOL_SOCKET */ valor = 1; resultado = setsockopt(meu_socket, SOL_SOCKET, SO_REUSEADDR, &valor, sizeof(valor)); if(resultado < 0) { perror("Servidor"); return 0; } servidor.sin_family = AF_INET; servidor.sin_port = htons(10000); servidor.sin_addr.s_addr = INADDR_ANY; resultado = bind(meu_socket, (struct sockaddr *)&servidor, sizeof(servidor)); if(resultado < 0) { perror("Bind"); return 0; } resultado = listen(meu_socket, nfilhos); if(resultado < 0) { perror("Listen"); return 0; } x = sizeof(cliente); printf("Agurdando Conexoes!!\n"); while ((novo_socket = accept(meu_socket, (struct sockaddr *)&cliente, (socklen_t *)&x))) { puts("Conexao aceita!!\n"); adiciona_cliente(novo_socket); resultado = pthread_create(&thread_id, NULL, thread_proc, (void *)&novo_socket); printf("\nResultado = %d\n",resultado); if(resultado != 0 ) { printf("Não foi possível criar a thread!\n"); return 0; } } sched_yield(); // a função sched_yield() força a thread que está rodando a abandonar o processador // até que ela se torne novamente cabeça da lista de threads. Não leva argumentos printf("antes\n"); pthread_join(thread_id, NULL); printf("depois\n"); printf("saiu\n"); return 0; } // fim do main void* thread_proc(void *arg) { int ouve, sock; char buffer[50]; char cmd[5]; int ler_dados; int i; sock = *(int *)arg; printf("Cliente conectado a thread filha %i com pid %i.\n", pthread_self(), getpid() ); while( 1 )/*fica recebendo mensagem do cliente*/ { printf("Voltou\n"); ler_dados = recv(sock, buffer, 25, 0); printf("RECV\n"); buffer[ler_dados] = '\0'; if(strcmp(buffer,"exit") == 0){ printf("Desconectou\n"); remove_cliente(sock); pthread_exit(NULL); } else{ printf("Cliente %d >> %s\n",sock,buffer); envia_msg_para_todos(sock, buffer); } printf("Final\n"); bzero(buffer,50); } } void inicial() { for(int i = 0; i < 20; i++) { cli[i].ativo = 0; } } void adiciona_cliente(int sock) { char apelido[20]; int ler_dados; cli[total].master = 0; if(total == 0) cli[total].master = 1; ler_dados = recv(sock, apelido, 20, 0);/*Obtem o apelido do usuário*/ if(ler_dados < 0 ) { perror("Recebendo Apelido"); } apelido[ler_dados] = '\0'; printf("@%s acabou de se conectar!\n",apelido); cli[total].sock = sock;/*o número de */ strcpy(cli[total].apelido, apelido);/*Copia para a estrutura cliente o apelido da nova conexão*/ cli[total++].ativo = 1; } void envia_msg_para_todos(int sock, char* msg) { int i=0; char buffer[100]; /*trato a mensagem para aparecer o apelido do usuário na mensagem*/ while(cli[i].sock != sock) i++; sprintf(buffer,"@%s diz: %s",cli[i].apelido,msg); for(i = 0; i < total; i++) { if(cli[i].sock != sock && cli[i].ativo == 1) { send(cli[i].sock, buffer, strlen(buffer), 0); } } } void remove_cliente(int sock) { char string[20]; int i=0; while(cli[i].sock != sock) i++; sprintf(string,"\nO user #%d se desconectou!!\n",cli[i].sock); envia_msg_para_todos(cli[i].sock, string);/*envia msg para os demais clientes informando que se desconectou*/ cli[i].ativo = 0; /*nao estará ativo*/ } void get_cmd(char *msgUser, char *cmd) { int i; for(i = 0; msgUser[i] != ' '; i++) { cmd[i] = msgUser[i]; } cmd[i] = '\0'; }