Здравствуйте.
В данное время я медленно, но с удовольствием постигаю прекрасный язык СИ (до этого было делфи, пхп, js и ещё что-то).
ПРОЛОГ
Мне понадобился маленький тср-серверок, который бы слушал порт, принимал от клиента некий символ (например букву А) и далее отправлял этот символ в микроконтроллер подключённый к серверу по USB. (Всё это связано с пресловутым «умным домом»)
СУТЬ
На просторах интернета я нашёл не малое количество примеров и остановился вот на этом - http://masandilov.ru/network/guide_to_network_programming6#6.1
После некоторых изменений, код приобрёл нижеследующий облик:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#define BACKLOG 10 // как много может быть ожидающих соединений
#define MAXBUFLEN 100
char PORT[5]={0,};
char device[14]={0,};
char er_log_str[50]={0,};
void sigchld_handler()
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
void *get_in_addr(struct sockaddr *sa) // получаем адрес сокета, ipv4 или ipv6:
{
if(sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
void error_log() // запись ошибки в файл ErrorSer.log
{
time_t t;
time(&t);
FILE *f;
f = fopen("ErrorSer.log", "a");
fprintf(f , "%s. ", er_log_str);
fprintf(f , "%s", ctime( &t));
printf("Write to ErrorSer.log\n");
fclose(f);
exit(0);
}
int main(int argc, char *argv[])
{
if(argc!=3)
{
printf("Primer zapyska - ./arduserver 3490 /dev/ttyACM1\n");
sprintf(er_log_str,"%s","Not use PORT");
error_log();
}
sprintf(PORT,"%s", argv[1]);
sprintf(device,"%s", argv[2]);
int sockfd, new_fd; // слушаем на sock_fd, новые соединения - на new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // информация об адресе клиента
socklen_t sin_size;
struct sigaction sa;
int yes=1;
// char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0)
{
sprintf(er_log_str,"%s","Error - getaddrinfo");
error_log();
}
for(p = servinfo; p != NULL; p = p->ai_next) // цикл через все результаты, чтобы забиндиться на первом возможном
{
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("server: socket");
continue;
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if(p == NULL)
{
sprintf(er_log_str,"%s","Error - failed to bindn");
error_log();
}
freeaddrinfo(servinfo); // всё, что можно, с этой структурой мы сделали
if(listen(sockfd, BACKLOG) == -1)
{
sprintf(er_log_str,"%s","Error - listen");
error_log();
}
sa.sa_handler = sigchld_handler; // обрабатываем мёртвые процессы
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if(sigaction(SIGCHLD, &sa, NULL) == -1)
{
sprintf(er_log_str,"%s","Error - sigaction");
error_log();
}
printf("server: waiting for connections...\n");
while(1) // главный цикл accept()
{
sin_size = sizeof their_addr;
new_fd = accept(sockfd, NULL, &sin_size);
if(new_fd == -1)
{
perror("accept");
continue;
}
printf("server: OK connection\n");
///////////////////////////////////////////////////////////////////////////////////////////
if(!fork()) // тут начинается дочерний процесс
{
close(sockfd); // дочернему процессу не нужен слушающий сокет
char buffer[7] = {0,};
int n = read(new_fd, buffer, 6);
if(n < 0)
{
sprintf(er_log_str,"%s","Error - reading from socket");
error_log();
}
printf("\n");
printf("Ot klienta polycheno: %c\n\n", buffer[5]);
char to_Ardu[14] = {0,};
sprintf(to_Ardu,"echo 'Y+=Z%c' > %s", buffer[5], device);
system(to_Ardu); // отправляем ардуине пакетик
close(new_fd);
exit(0);
}
close(new_fd); // а этот сокет больше не нужен родителю
} // конец while
return 0;
}
/// ./arduserver 3490 /dev/ttyACM1
ВОПРОС
Очень хочется, чтоб уважаемое сообщество указало на ошибки в коде и объяснило некоторые моменты.
В частности:
1. Корректно ли я инициализирую массивы.
char PORT[5]={0,};
2. Нужен ли в данном примере дочерний процесс
if(!fork())
...
Зараннее спасибо.