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$
Aprenda a Gerenciar Permissões de Arquivos no Linux
Como transformar um áudio em vídeo com efeito de forma de onda (wave form)
Como aprovar Pull Requests em seu repositório Github via linha de comando
Dando - teoricamente - um gás no Gnome-Shell do Arch Linux
Como instalar o Google Cloud CLI no Ubuntu/Debian
Mantenha seu Sistema Leve e Rápido com a Limpeza do APT!
Procurando vídeos de YouTube pelo terminal e assistindo via mpv (2025)