Enviado em 16/08/2019 - 11:23h
Seguinte, desenvolvi um pequeno servidor em C usando POSIX Sockets e gostaria de obter algumas opiniões e sugestões para melhoria de código.
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define RCVTIMEO 3
static int set_socket_opts(int sockfd, bool use_only_ipv6){
int rv;
const int optval=1;
socklen_t optlen=sizeof(optval);
struct timeval rcv;
socklen_t tvlen=sizeof(rcv);
rcv.tv_sec=RCVTIMEO;
rcv.tv_usec=0;
do{
if((rv=setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen))!=0){
break;
}
if((rv=setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcv, tvlen))!=0){
break;
}
if(use_only_ipv6==true){
rv=setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, optlen);
}
}while(false);
return rv;
}
static int make_server_socket(char *argv[]){
bool use_only_ipv6;
int af, ecode, sockfd;
struct addrinfo hints, *rp=NULL, *res=NULL;
memset(&hints, 0, sizeof(hints));
if(strcmp(argv[1], "-4")==0){
af=AF_INET;
use_only_ipv6=false;
}else if(strcmp(argv[1], "-M")==0){
af=AF_INET6;
use_only_ipv6=false;
}else{
af=AF_INET6;
use_only_ipv6=true;
}
hints.ai_flags=AI_PASSIVE;
hints.ai_family=af;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
hints.ai_addr=NULL;
hints.ai_canonname=NULL;
hints.ai_next=NULL;
if((ecode=getaddrinfo(NULL, argv[2], &hints, &res))!=0){
fprintf(stderr, "* getaddrinfo() -> ERROR(ecode: %d): %s\n", ecode, gai_strerror(ecode));
exit(EXIT_FAILURE);
}
for(rp=res; rp!=NULL; rp=rp->ai_next){
sockfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(sockfd<0){
continue;
}else{
if(set_socket_opts(sockfd, use_only_ipv6)!=0){
continue;
}
}
if(bind(sockfd, rp->ai_addr, rp->ai_addrlen)==0){
break;
}
close(sockfd);
}
if(rp==NULL){
fprintf(stderr, "* bind() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}else{
if(listen(sockfd, 10)!=0){
close(sockfd);
fprintf(stderr, "* listen() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
freeaddrinfo(res);
res=NULL;
rp=NULL;
return sockfd;
}
static int check_addr(char **buff_addr_table, const char *buff_addr, int af, size_t count){
int rv=0;
size_t addrlen;
if(af==AF_INET){
struct sockaddr_in *addr4_from_table=NULL;
struct sockaddr_in *addr4=(struct sockaddr_in*)buff_addr;
addrlen=sizeof(addr4->sin_addr.s_addr);
for(size_t i=0; i<count; i++){
addr4_from_table=(struct sockaddr_in*)buff_addr_table[i];
if(memcmp(&addr4_from_table->sin_addr.s_addr, &addr4->sin_addr.s_addr, addrlen)==0){
rv=-1;
break;
}
}
}else if(af==AF_INET6){
struct sockaddr_in6 *addr6_from_table=NULL;
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)buff_addr;
addrlen=sizeof(addr6->sin6_addr.s6_addr);
for(size_t i=0; i<count; i++){
addr6_from_table=(struct sockaddr_in6*)buff_addr_table[i];
if(memcmp(&addr6_from_table->sin6_addr.s6_addr, &addr6->sin6_addr.s6_addr, addrlen)==0){
rv=-1;
break;
}
}
}else{
rv=-2;
}
return rv;
}
static void show_addr(const struct sockaddr *addr){
char *str_addr=NULL;
if(addr->sa_family==AF_INET){
str_addr=malloc(INET_ADDRSTRLEN*sizeof(*str_addr));
if(str_addr==NULL){
fprintf(stderr, "Memory allocation for %ld bytes failed\n", INET_ADDRSTRLEN*sizeof(*str_addr));
fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
if(inet_ntop(AF_INET, &addr4->sin_addr.s_addr, str_addr, INET_ADDRSTRLEN)==NULL){
fprintf(stderr, "* inet_ntop() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
}else{
fprintf(stdout, "%s\n", str_addr);
}
}else{
str_addr=malloc(INET6_ADDRSTRLEN*sizeof(*str_addr));
if(str_addr==NULL){
fprintf(stderr, "Memory allocation for %ld bytes failed\n", INET6_ADDRSTRLEN*sizeof(*str_addr));
fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
if(inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr, str_addr, INET6_ADDRSTRLEN)==NULL){
fprintf(stderr, "* inet_ntop() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
}else{
fprintf(stdout, "%s\n", str_addr);
}
}
free(str_addr);
}
static char **realloc_addr_table(char **buff_addr_table, socklen_t addrlen, size_t count){
buff_addr_table=realloc(buff_addr_table, count*sizeof(*buff_addr_table));
if(buff_addr_table==NULL){
fprintf(stderr, "Memory allocation for %ld bytes failed\n", count*sizeof(*buff_addr_table));
fprintf(stderr, "realloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
buff_addr_table[count-1]=malloc(addrlen*sizeof(**buff_addr_table));
if(buff_addr_table[count-1]==NULL){
fprintf(stderr, "Memory allocation for %ld bytes failed\n", addrlen*sizeof(**buff_addr_table));
fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
return buff_addr_table;
}
static void free_addr_table(char **buff_addr_table, size_t count){
for(size_t i=0; i<count; i++){
free(buff_addr_table[i]);
}
free(buff_addr_table);
}
static void search(char *argv[]){
socklen_t addrlen;
char *buff_addr=NULL;
char **buff_addr_table=NULL;
struct sockaddr *addr=NULL;
char *eptr;
size_t i=0, max_connections=strtoll(argv[3], &eptr, 10);
int sockfd, new_sockfd;
char message[512];
sockfd=make_server_socket(argv);
if(strcmp(argv[1], "-4")==0){
addrlen=sizeof(struct sockaddr_in);
}else{
addrlen=sizeof(struct sockaddr_in6);
}
buff_addr=calloc(addrlen, sizeof(*buff_addr));
if(buff_addr==NULL){
fprintf(stderr, "Memory allocation for %ld bytes failed!\n", addrlen*sizeof(*buff_addr));
fprintf(stderr, "calloc() -> ERROR(errno: %d): %s", errno, strerror(errno));
exit(EXIT_FAILURE);
}
addr=(struct sockaddr*)buff_addr;
while(i<max_connections){
new_sockfd=accept(sockfd, addr, &addrlen);
if(new_sockfd!=-1){
if(check_addr(buff_addr_table, buff_addr, addr->sa_family, i)==0){
show_addr(addr);
if(recv(new_sockfd, message, sizeof(message), 0)==-1){
fprintf(stderr, "recv() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
}else{
fprintf(stdout, "Client says: %s\n\n", message);
}
i++;
buff_addr_table=realloc_addr_table(buff_addr_table, addrlen, i);
memcpy(buff_addr_table[i-1], buff_addr, addrlen*sizeof(*buff_addr));
}
close(new_sockfd);
}
}
free(buff_addr);
free_addr_table(buff_addr_table, i);
}
int main(int argc, char *argv[]){
char *eptr;
long long int res;
if(argc<4){
fprintf(stdout, "Usage: %s [-option] [port/service] [max_connections] \n", argv[0]);
fprintf(stdout, " -4 Use IPv4 \n");
fprintf(stdout, " -6 Use IPv6 \n");
fprintf(stdout, " -M Use IPv4 and IPv6 (IPv4-mapped IPv6 address) \n");
fprintf(stdout, " -h Show this page \n");
exit(EXIT_SUCCESS);
}
res=strtoll(argv[3], &eptr, 10);
if(res==0 || res==LONG_MAX || res==LONG_MIN){
if(errno==EINVAL || errno==ERANGE){
fprintf(stderr, "* strtoll() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
}else{
fprintf(stderr, "* Invalid value for max_connections!\n");
}
}else{
int opt=getopt(argc, argv, "46Mh");
switch(opt){
case '4':
case '6':
case 'M':
search(argv);
break;
case '?':
/*getopt() output*/
break;
case 'h':
default:
fprintf(stdout, "Usage: %s [-option] [port/service] [max_connections] \n", argv[0]);
fprintf(stdout, " -4 Use IPv4 \n");
fprintf(stdout, " -6 Use IPv6 \n");
fprintf(stdout, " -M Use IPv4 and IPv6 (IPv4-mapped IPv6 address) \n");
fprintf(stdout, " -h Show this page \n");
break;
}
}
return EXIT_SUCCESS;
}
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
static int try_connect(char *argv[]){
int ecode, sockfd;
struct addrinfo *rp=NULL, *res=NULL, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
hints.ai_addr=NULL;
hints.ai_canonname=NULL;
hints.ai_next=NULL;
if((ecode=getaddrinfo(argv[1], argv[2], &hints, &res))!=0){
fprintf(stderr, "* getaddrinfo() -> ERROR(ecode: %d): %s\n", ecode, gai_strerror(ecode));
exit(EXIT_FAILURE);
}
for(rp=res; rp!=NULL; rp=rp->ai_next){
if((sockfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))<0){
continue;
}
if(connect(sockfd, rp->ai_addr, rp->ai_addrlen)==0){
break;
}
close(sockfd);
}
if(rp==NULL){
fprintf(stderr, "* socket()/conncet() -> ERROR(errno: %d): %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
freeaddrinfo(res);
return sockfd;
}
int main(int argc, char *argv[]){
const char message[]="Real Muthaphuckkin G's";
int sockfd=try_connect(argv);
if(send(sockfd, message, sizeof(message), 0)!=sizeof(message)){
fprintf(stderr, "* send() -> ERROR(errno: %d): %s\n", errno, message);
}else{
printf("send() -> OK\n");
}
return EXIT_SUCCESS;
}
zherkezhi@zherkezhi:~/Documents$ gcc -Wall server.c -o server
zherkezhi@zherkezhi:~/Documents$ ./server -h
Usage: ./server [-option] [port/service] [max_connections]
-4 Use IPv4
-6 Use IPv6
-M Use IPv4 and IPv6 (IPv4-mapped IPv6 address)
-h Show this page
zherkezhi@zherkezhi:~/Documents$ ./server -M 9009 2
::ffff:192.168.50.202
Client says: Real Muthaphuckkin G's
::ffff:127.0.0.1
Client says: Real Muthaphuckkin G's
zherkezhi@zherkezhi:~/Documents$ ./server -4 9009 2
127.0.0.1
Client says: Real Muthaphuckkin G's
192.168.50.202
Client says: Real Muthaphuckkin G's
zherkezhi@zherkezhi:~/Documents$
zherkezhi@zherkezhi:~/Documents$ gcc -Wall client.c -o client
zherkezhi@zherkezhi:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$
Atenção a quem posta conteúdo de dicas, scripts e tal (1)
Manutenção de sistemas Linux Debian e derivados com apt-get, apt, aptitude e dpkg
Melhorando o tempo de boot do Fedora e outras distribuições
Como instalar as extensões Dash To Dock e Hide Top Bar no Gnome 45/46
Instalar a última versão do PostgreSQL no Lunix mantendo atualizado
Flathub na sua distribuição Linux e comandos básicos de gerenciamento
Instalando o FreeOffice no LMDE 6
Anki: Remover Tags de Estilo HTML de Todas as Cartas
Colocando uma opção de redimensionamento de imagem no menu de contexto do KDE
Desenvolvimento de um driver (10)
Não esta salvando as configurações de usuário. (3)
Quais Shell Scripts vocês usam? (5)
[Shell Script] Script para desinstalar pacotes desnecessários no OpenSuse
[Shell Script] Script para criar certificados de forma automatizada no OpenVpn
[Shell Script] Conversor de vídeo com opção de legenda
[C/C++] BRT - Bulk Renaming Tool
[Shell Script] Criação de Usuarios , Grupo e instalação do servidor de arquivos samba