Comunicação em texto via socket() entre cliente/servidor em C

1. Comunicação em texto via socket() entre cliente/servidor em C

Perfil removido
removido

(usa Nenhuma)

Enviado em 19/07/2016 - 18:39h

Boa noite pessoal, bom estou tentando fazer um cliente e um servidor para uma comunicação "segura" via internet, óbvio que este é um programa de estudos e não tem nada de seguro, mas é o meu começo.
Vamos ao assunto; as estruturas estão funcionando, os argumentos são passados e o conteúdo das variáveis é armazenado com sucesso, mas o meu objetivo aqui é fazer uma interação continua, e não uma mensagem por vez.
Quando eu envio uma mensagem do cliente para o servidor (ou do servidor para o cliente e vice-versa), eu tenho que esperar uma mensagem ser enviada do servidor, ao invés de poder mandar mais de uma mensagem seguida, creio que o meu problema em fazer isso seja a falta de experiência com as funções "send()" e "recv()".
Segue o code:

/* Cliente */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(int argc,char *argv[]){
int sockfd,porta=0;
struct sockaddr_in servidor;
struct hostent *server;
char username[20],userserver[20],mensagem[256];

if(argc<3)
{
printf("Usage: %s <ip/dns> <port>\n",argv[0]);
exit(1);
}
printf("Chose a username: ");
__fpurge(stdin);
if(scanf("%19[a-zA-Z0-9-_]",username)==0 || strlen(username)==1)
{
printf("Please, chose a valid username.\n");
exit(1);
}
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
fprintf(stderr,"SOCKET: %s\n",strerror(errno));
exit(1);
}
server=gethostbyname(argv[1]);
if(server == NULL)
{
fprintf(stderr,"unknown host.\n");
exit(1);
}
porta=atoi(argv[2]);
if(porta<0 || porta>65535)
{
printf("Especify a valid port!\n");
exit(1);
}
bzero((char*)&servidor,sizeof(servidor));
servidor.sin_family=AF_INET;
bcopy((char *)server->h_addr,(char *)&servidor.sin_addr.s_addr,server->h_length);
servidor.sin_port=htons(porta);
if(connect(sockfd,(struct sockaddr *)&servidor,sizeof(servidor))==-1)
{
fprintf(stderr,"CONNECT: %s\n",strerror(errno));
exit(1);
}
system("clear");
printf("\nSucessfully connected to: %s\n",inet_ntoa(servidor.sin_addr));
printf("To quit, say \"-exit\"\n");
do
{
send(sockfd,username,sizeof(username),0);
bzero(mensagem,256);
printf("%s: ",username);
__fpurge(stdin);
scanf("%[^\n]",mensagem);
send(sockfd,mensagem,sizeof(mensagem),0);

bzero(mensagem,256);
bzero(userserver,20);
recv(sockfd,userserver,sizeof(userserver),0);
recv(sockfd,mensagem,sizeof(mensagem),0);
printf("%s: %s\n",userserver,mensagem);

}while(strcmp(mensagem,"-exit")!=0);
close(sockfd);
return 0;
}



/* Servidor */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc,char *argv[]){
int sockfd,conectado,porta=0;
socklen_t sizesocket;
struct sockaddr_in servidor,cliente;
char username[20],userclient[20],mensagem[256];

if(argc<2)
{
printf("Usage: %s port\n",argv[0]);
exit(1);
}
porta=atoi(argv[1]);
if(porta<0 || porta>65535)
{
printf("Especify a valid port!\n");
exit(1);
}
printf("Chose a username: ");
__fpurge(stdin);
if(scanf("%19[a-zA-Z0-9-_]",username)==0 || strlen(username)==1)
{
printf("Please, write a valid username.\n");
exit(1);
}
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
fprintf(stderr,"SOCKET: %s\n",strerror(errno));
exit(1);
}
bzero ((char *)&servidor,sizeof(servidor));
servidor.sin_family=AF_INET;
servidor.sin_addr.s_addr=INADDR_ANY;
servidor.sin_port=htons(porta);
sizesocket=sizeof(cliente);

if(bind(sockfd,(struct sockaddr *)&servidor,sizeof(servidor))==-1)
{
fprintf(stderr,"BIND: %s\n",strerror(errno));
exit(1);
}
else printf("Bind sucessfully!\n");

if(listen(sockfd,0)==-1)
{
fprintf(stderr,"LISTEN: %s\n",strerror(errno));
exit(1);
}
else printf("Listening on %d\n",porta);

conectado=accept(sockfd,(struct sockaddr *)&cliente,&sizesocket);
if(conectado==-1)
{
fprintf(stderr,"ACCEPT: %s\n",strerror(errno));
exit(1);
}
close(sockfd);
system("clear");
printf("\nClient: %s sucesfully connected to the server.\n",inet_ntoa(cliente.sin_addr));
printf("To quit, say \"-exit\"\n");
printf("Please, wait some contact :)\n");
do
{
send(conectado,username,sizeof(username),0);
bzero(mensagem,256);
bzero(userclient,20);
recv(conectado,userclient,sizeof(userclient),0);
recv(conectado,mensagem,sizeof(mensagem),0);
printf("%s: %s\n",userclient,mensagem);

printf("%s: ",username);
__fpurge(stdin);
scanf("%[^\n]",mensagem);
send(conectado,mensagem,sizeof(mensagem),0);


}while(strcmp(mensagem,"-exit")!=0);
close(conectado);
return 0;
}



  


2. Re: Comunicação em texto via socket() entre cliente/servidor em C

Arthur J. Hoch
Arthur_Hoch

(usa FreeBSD)

Enviado em 19/07/2016 - 20:07h

http://linux.die.net/man/3/select


3. Re: Comunicação em texto via socket() entre cliente/servidor em C

Jeffersson Abreu
ctw6av

(usa Nenhuma)

Enviado em 19/07/2016 - 20:38h

Palpiteiro que sou vou palpitar, não sei enquanto a linguagem C (estou tentando ler seu programa mas acho que vou aprender a falar mandarim antes de conseguir ler algo em C) kkkk. Em python o recv() tem de estar dentro de um loop, por exemplo um while True para receber mensagens processar e depois voltar a escutar até que um dos dois encerre a conexão

Ex:
def server(interface, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
sock.bind((interface, port))
sock .listen(1)
print('Lstening at', sock.getsockname())
while True:
sc, sockname = sock.accept()
message = sock.recv(sc, 16)
print('Incoming sixteen-octet message', repr(message))
sc.close()


Depois que a mensagem chega ele imprime ela na tela e depois fecha o socket, o loop continua o mesmo endereço é usado esperando mais mensagens.

Espero que te ajude ^_^





----------------------------------------------------------
Debiano com uma pitada de slack
----------------------------------------------------------


4. Re: Comunicação em texto via socket() entre cliente/servidor em C

Perfil removido
removido

(usa Nenhuma)

Enviado em 20/07/2016 - 04:20h

Vou dar uma estudada na função "select()" e ver o que da pra fazer.
Eu já estudei Python uma vez (bem superficialmente) e não consegui me acostumar com a tipologia hehe, mas enfim, deu pra entender sim, obrigado pelas respostas :)
--
Just bring us some beers, and then we can talk about our systems. :)



5. Re: Comunicação em texto via socket() entre cliente/servidor em C

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/07/2016 - 17:10h

Caro Cristhohher994,

Num exame rápido, seu programa tem algumas coisas esquisitas ou obsoletas:

    - Dê um jeito de eliminar o uso de __fpurge(), através de strings de formatação melhores nas chamadas a scanf() ou substituição dessa função por outros mecanismos de leitura.

    - Evite chamar system() para coisas triviais como limpar a tela, ainda mais dentro de um loop.

    - A função bzero() é obsoleta, e você a está usando de um modo mais obsoleto ainda (aquela conversão de tipo para “char *” era coisa necessária apenas antes da aprovação do padrão do C de 1989, quando ainda não existia “void *”). Em lugar disso, use memset().

Você está fazendo algo que se parece muito com um chat. Mas do jeito como você está fazendo, o chat acaba ficando obrigatoriamente com mensagens alternadas.

O método tradicional de permitir que duas fontes de dados possam mandar seus dados de modo assíncrono e aleatório é usar uma função como select() ou poll(), que examinam várias possíveis fontes ou destinos de dados para saber se há eventos a serem realizados sobre elas. Tais funções, contudo, operam com descritores de I/O de baixo nível, de modo que, se você quiser usá-las, provavelmente terá de dizer adeus a scanf() como método de entrada de dados.

Mais modernamente, algumas pessoas preferem trabalhar com múltiplas threads, cada uma para uma possível fonte de dados. Isso facilita algumas operações, mas exige muito mais cuidado quando as diversas threads podem ter acesso simultâneo aos mesmos dados ou a recursos compartilhados (como, por exemplo, a própria tela para exibição de mensagens).

Em programas de chat que não sejam rigidamente síncronos, dificilmente você vai querer exibir uma mensagem que acabou de chegar pela rede no mesmo espaço em que o usuário local ainda está digitando a mensagem dele, sob o risco de ambas se misturarem visualmente e estragarem a experiência do usuário com o programa. Possivelmente você vai querer criar espaços separados para mensagens que estão sendo digitadas localmente e mensagens que estão chegado de fora. Isso aponta para o uso de uma biblioteca como curses. E se você decidir usar curses para operações de saída de dados, vai se ver praticamente obrigado a usá-la também para entrada de dados.


6. Re: Comunicação em texto via socket() entre cliente/servidor em C

Perfil removido
removido

(usa Nenhuma)

Enviado em 21/07/2016 - 08:24h

"Dê um jeito de eliminar o uso de __fpurge(), através de strings de formatação melhores nas chamadas a scanf() ou substituição dessa função por outros mecanismos de leitura."


É que eu só consigo ler espaços com essa formatação específica no "scanf()" ("[^\n]"), mas eu já andei pensando em outras strings de formatação melhores até pelo fato do "__fpurge()" acabar com a portabilidade dos programas.
Em outros codes eu uso "fgets()" quando eu quero ler strings compostas por exemplo; eu armazeno em variáveis diferentes com "fgets()". Foi uma das maneiras que eu encontrei de substituir o "scanf()" e consequente o "__fpurge()".
"A função bzero() é obsoleta, e você a está usando de um modo mais obsoleto ainda (aquela conversão de tipo para “char *” era coisa necessária apenas antes da aprovação do padrão do C de 1989, quando ainda não existia “void *”). Em lugar disso, use memset()."

Eu estava usando a "bzero()" porque tem muito pouco material para estudo sobre sockets em C, então eu aprendi deste jeito. Mas eu devo suspender o uso totalmente desta função ou apenas a conversão de tipo? de qualquer maneira vou passar a usar "memset()"

"Mais modernamente, algumas pessoas preferem trabalhar com múltiplas threads" ...

Eu andei lendo sobre threads em C, mas ainda é um assunto um tanto complexo para mim. Tu fala sobre a library "ncurses" certo? eu andei lendo alguns exemplos superficialmente, mas eu achei um tanto weird porque essa library usa algumas funções que eu nunca vi antes como a "mvaddstr()" que printa uma string na janela atual. Mas enfim, é apenas questão de interesse e de estudar um pouco.
De qualquer maneira eu acho que a questão é: se eu quero começar a escrever sockets eu devo deixar velhos hábitos para trás né? enfim muito obrigado paulo1205, sempre aprendo bastante coisas novas com suas respostas :)
--
Just bring us some beers, and then we can talk about our systems. :)







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts