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

Odpowiedz Nowy wątek
2019-04-12 21:01
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

edytowany 1x, ostatnio: Szymon Żak, 2019-04-12 21:02
Może się wstydzi? - pavarotti 2019-04-12 22:28

Pozostało 580 znaków

2019-04-12 23:30
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 :)

edytowany 1x, ostatnio: au7h, 2019-04-12 23:31
Ale przecież OP ma odwrotny przypadek: z testu wynika, że rozpoznało mu samo \n jako \r\n - kq 2019-04-13 02:12
Dokładnie - dodawanie if( buf[i] == '\n') nic nie daje - zresztą ma rozpoznać tylko '\r\n' - w przeciwnym wypadku jest error. - Szymon Żak 2019-04-13 03:49

Pozostało 580 znaków

2019-04-13 10:17

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
            }
        }
    }
edytowany 1x, ostatnio: _0x666_, 2019-04-13 10:45

Pozostało 580 znaków

2019-04-14 18:15
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.

Pozostało 580 znaków

2019-04-15 15:03
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.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 1x, ostatnio: MarekR22, 2019-04-15 15:06

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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