[Język C] Skanowanie ciągu znaków z wejścia, program nie rozpoznaje różnicy pomiędzy '\r' i '\n'

0

Cześć,
mam do stworzenia serwer który jest sumatorem liczb. Wysyłam z klienta (netcat) pewne ciągi znaków - aby zadany ciag znaków był poprawny mogą się w nim znajdować tylko liczby, spacje (jedna pomiędzy każdą liczbą) oraz terminator linii - '\r\n'. Jeśli ciąg się nie zgadza, sumator powinien wysyłać ciąg znaków 'ERROR\r\n'.
Mój sumator rozpoznaje praktycznie wszystkie dane wejściowe poprawnie, oprócz przypadku gdy zamiast '\r\n' wyślę '\n'. Niestety, nie potrafię samodzielnie znaleźć rozwiazania.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

const unsigned short MAX_BYTES = 1024;
const unsigned short PORTNUM = 2019;

void sum_line(char* buf, unsigned int buflen, int newsockfd)
{
        unsigned i = 0, sum = 0, digit;
        bool succesful = true;

        if(buflen > MAX_BYTES || !( buf[i] >= '0' && buf[i] <= '9' ) ) succesful = false;

        while( i < buflen )
        {
                digit = 0;

                while( buf[i] >= '0' && buf[i] <= '9'  )
                {
                        if(i > MAX_BYTES || digit > INT_MAX/10 + INT_MAX%10 ) succesful = false;

                        digit *= 10;

                        if( digit > INT_MAX  - (buf[i] - '0') ) succesful = false;

                        digit += (buf[i] - '0');

                        i++;
                }

                if( sum > INT_MAX - digit ) succesful = false;

                sum += digit;

                if(buf[i] == '\r' && buf[i+1] == '\n' )
                {
                        if(succesful)
                        {
                                int length = snprintf( NULL, 0, "%d", sum );
                                char str[length + 2];
                                sprintf( str, "%d\r\n", sum );
                                if( send( newsockfd, str, length + 2, 0 ) < 0 )
                                {
                                        perror("send");
                                        exit(EXIT_FAILURE);
                                }
                        }
                        else
                        {
                                if( send( newsockfd, "ERROR\r\n", 7, 0 ) < 0 )
                                {
                                        perror("send");
                                        exit(EXIT_FAILURE);
                                }
                        }
                        if(i + 2 < buflen)
                                sum_line( (buf+i+2), buflen-(i+2), newsockfd );
                        return;
                }
                else if(buf[i] == ' ')
                {
                        i++;
                        if( buf[i] < '0' || buf[i] > '9') succesful = false;
                }
                else
                {
                        if( send( newsockfd, "ERROR\r\n", 7, 0 ) < 0 )
                        {
                                perror("send");
                                exit(EXIT_FAILURE);
                        }
                        while(buf[i] != '\r' && (buf[i+1] != '\n' && i + 2 < buflen) ) i++;
                        if(i + 2 < buflen) sum_line( (buf+i+2), buflen-(i+2), newsockfd );
                        return;
                }
        }
}

int main(int argc, char *argv[])
{
        int sockfd, newsockfd;
        char buf[MAX_BYTES + 1];
        unsigned int clilen;
        struct sockaddr_in serv_addr, cli_addr;

        newsockfd = 0;
        clilen = sizeof(cli_addr);
        sockfd = socket(AF_INET, SOCK_STREAM, 0);

        if ( sockfd < 0 )
        {
                perror("socket");
                exit(EXIT_FAILURE);
        }

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(PORTNUM);
        serv_addr.sin_addr.s_addr = INADDR_ANY;

        if ( bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 )
        {
                perror("bind\n");
                exit(EXIT_FAILURE);
        }

        if ( listen(sockfd, 3) < 0 )
        {
                perror("listen\n");
                exit(EXIT_FAILURE);
        }

        while( true )
        {
                newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

                if (newsockfd < 0)
                {
                        perror("accept");
                        exit(EXIT_FAILURE);
                }

                while( true )
                {
                        memset(buf, 0x00, MAX_BYTES * sizeof(char));

                        int buflen = read(newsockfd, buf, MAX_BYTES );

                        if( buflen == 0 )
                                break;

                        if( buflen < 0 )
                        {
                                perror("read");
                                exit(EXIT_FAILURE);
                        }

                        sum_line(buf, buflen, newsockfd);
                }

                printf("Client disconnected\n");
                close(newsockfd);
        }
        return 0;
}

screenshot-20190412210205.png

0

if(buf[i] == '\r' && buf[i+1] == '\n' ) trudno żeby miał rozpoznawać różnicę skoro masz tylko jeden warunek na sprawdzanie czy kolejne znaki w buforze są \r\n. Dopisz jeszcze jednego ifa :)

0

Coś w ten deseń:

	while( i < buflen )
	{
		digit = 0;

		while(i < buflen && isdigit(buf[i]) )
		{
			digit *= 10;
			digit += (buf[i++] - '0');
		}

		sum += digit;

		if(i < buflen && buf[i++] == '\n')
		{
			if(i > 1 && buf[i - 2] == '\r')
			{
				// good
				continue;
			}
			else 
			{
				// not good
			}
		}
	}
0

Najsampierw podzieliłbym ten kod na niezależne części. Np:
(Kod długi nie wiem jak to skrócić, aby było schowane, jeśli ktoś wie to proszę napisać)

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

const unsigned short MAX_BYTES = 1024;
const unsigned short PORTNUM = 2019;

void check_failure(int value_to_check,const char* function_name,char *buf_to_clear){
	if(value_to_check < 0){
		if(buf_to_clear != NULL)
			free(buf_to_clear);
		perror(function_name);
		exit(EXIT_FAILURE);
	}
}

void send_sum(long int sum,int sock){
	int length = snprintf(NULL,0,"%ld",sum);
	char *str = (char *)malloc(length + 3);
	sprintf(str,"%ld\r\n",sum);
	check_failure(send(sock, str, length + 2, 0 ),"send",str);
	free(str);
}

void send_fail(const char *msg,int sock){
	char *str = (char *)malloc(strlen(msg) + 3);
	sprintf(str,"%s\r\n",msg);
	check_failure(send(sock, str, strlen(str), 0 ),"send",str);
	free(str);
}

int find_new_line(char *buf,int start,int limit){
	int i;
	for(i=start;i<limit;++i)
		if(buf[i] == '\r' || buf[i] == '\n'){
			++i;
			while(i<limit && (buf[i] == '\r' || buf[i] == '\n'))
				++i;
			return i;
		}
	return limit;
}

int find_number(char *buf,int actual_start,int stop){
	int i;
	for(i=actual_start + 1;i<stop;++i){
		if(!isdigit(buf[i]))
			return i;
	}
	return stop;
}

long int value(char *buf, int start, int stop){
	long int value;
	char *str_value = (char *)malloc(stop-start+1);
	memcpy(str_value,buf+start,stop-start);
	value = strtol(str_value,NULL,10);
	free(str_value);
	return value;
}

long int sum_line(char* buf, int start, int stop){
	int i,end_number;
	long int sum = 0;
	for(i=start;i<stop;++i){
		if(isdigit(buf[i]) || buf[i] == '-'){
			end_number = find_number(buf,i,stop);
			if(end_number != i){
				sum += value(buf,i,end_number);
			}	
			i = end_number;
		}
	}
	return sum;
}

void sum_all(char *buf,unsigned int buflen,int sock){
	int start = 0, stop = 0;
	long int result;
	while(stop < buflen-1){
		stop = find_new_line(buf,start,buflen);
		if(start < stop){
			result = sum_line(buf,start,stop);
			start = stop;

			if(result >= 0){
				send_sum(result,sock);
				continue;
			}
		}
		send_fail("ERROR",sock);
	}
}


int main(int argc, char *argv[])
{
	int sockfd, newsockfd;
	char buf[MAX_BYTES + 1];
	unsigned int clilen;
	struct sockaddr_in serv_addr, cli_addr;
	newsockfd = 0;
	clilen = sizeof(cli_addr);
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	check_failure(sockfd,"socket",NULL);
	
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(PORTNUM);
	serv_addr.sin_addr.s_addr = INADDR_ANY;

	check_failure(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)),"bind",NULL);
	check_failure(listen(sockfd, 3),"listen",NULL);
	while( true ){
		newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		check_failure(newsockfd ,"accept",NULL);
		
		while( true ){
			memset(buf, 0x00, MAX_BYTES * sizeof(char));
			int buflen = read(newsockfd, buf, MAX_BYTES );
			if( buflen == 0 )
				break;
			check_failure(buflen,"read",NULL);
			sum_all(buf, buflen, newsockfd);
		}
		printf("Client disconnected\n");
		close(newsockfd);
	}
	return 0;
}

Kod dodaje do siebie liczby niezależnie jakim znakiem nowego wiersza są oddzielone.
Przetestowałem to na 3 różnych zakończeniach.
https://pl.wikipedia.org/wiki/End-of-line

Kod nie jest doskonały, to tylko przykład.
Zaletę którą chciałem pokazać to możliwość skupienia się na określonej części kodu, dzięki podziałowi na funkcje.

0

Generalnie zgadzam się z @woki
Kod trzeba zawsze dzielić na mniejsze problemy:

  • logika biznesowa - tutaj proces akumulowania liczb (w domyśle sumowanie)
  • parsowanie danych, wykrywanie naruszania formatu danych
  • odbieranie danych
  • przyjmowanie połączeń

Każda z tych części powinna mieć co najmniej jedną własną funkcję.
Najlepiej jeszcze jakby te warstwy było od siebie zupełnie niezależne.

I jeszcze cytując klasyka:

If you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program.

1 użytkowników online, w tym zalogowanych: 0, gości: 1