memset не может установить строку в ноль и идет в ошибку сегментации - PullRequest
0 голосов
/ 23 ноября 2018

Я делаю упражнение о сокетах UDP в C. Когда клиент отправляет конкретное сообщение (например, привет), сервер должен отправить «Приятно познакомиться».Если стандартный ответ не найден, сервер отправляет «Нет подходящего ответа».Моя проблема в том, что memset завершается неудачно, если я пытаюсь вернуть ответ следующим образом:

return "No suitable reply";

, и не получается, если я возвращаю ответ таким образом:

char* foo = malloc(sizeof(char*));
memset(foo, 0, strlen(ses));
memcpy(foo, "No suitable reply", 17);
return foo;

Я попытался найти решение этой проблемы в Google и обнаружил это и это , но они, похоже, не решают мою проблему (сначала я подумал, что memset не работает со строкойобъявлен как char string[] = "something", но во втором примере они используют memset для статической строки).

Вот весь код (мемсет, о котором я говорю, находится в конце):

   /*
    Alessandro Dussin 5AI
    2018-17-11
    Write a program to handle a single UDP "connection"
    */

//Standard libraries
#include <stdio.h>
#include <stdlib.h>

//Sockets libraries and connection ahndling
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>

//Read/write ops on file descriptors
#include <unistd.h>
//String ops
#include <string.h>

#include <assert.h>
void chopN(char *str, size_t n)
{
    assert(n != 0 && str != 0);
    size_t len = strlen(str);
    if (n > len)
        return;  // Or: n = len;
    memmove(str, str+n, len - n + 1);
}

//Required by the exercise. Given a certain word or phrase, reply with a specific string
char* switchreply(char* str){
    //Extracts the word or phrase (Basically removes the "/command " word)
    chopN(str, strlen("/stdreply "));
    int i = 0;
    for(; i < strlen(str); i++){
        if(str[i] == '\n'){
            str[i] = '\0';
            break;
        }
    }
    if(strcmp(str, "ciao") == 0){
        return "ciao anche a te!";
    }
    else if(strcmp(str, "I hate you") == 0){
        return "I hate you too!";
    }
    return "";
}


char* stdreply(char *str){

    char* tmp = malloc(sizeof(char)*128);
    int i = 0;
    //printf("Entered stdreply... str at the start of the func: %s\n", str);
    for(; i < strlen(str); i++){
        tmp[i] = str[i];
        //printf("tmp: %s\n", tmp); //DEBUG
        if(strcmp(tmp, "/echo ") == 0){ // if(strcmp() == 0) is necessary because
                                        //otherwise 0 would be interpreted as FALSE
            //printf("Echo detected\n"); //DEBUG
            chopN(str, strlen("/echo "));
            str[strlen(str)] = '\0';
            return str;
        }
        else if(strcmp(tmp, "/stdreply ") == 0){
            //printf("I got into the else if\n"); //DEBUG
            char* tmpreply = calloc(strlen(str), sizeof(char*));
            tmpreply = switchreply(str);
            //printf("tmpreply: %s\n", tmpreply);
            str = malloc(sizeof(char*)*strlen(tmpreply));
            memcpy(str, tmpreply, strlen(tmpreply));
            //str[strlen(str)] = '\0'; //DEBUG
            //printf("str: %s\n", str); //DEBUG
            return str;
        }
        else if(strcmp(tmp, "/TODO") == 0){
            char* ses = malloc(sizeof(char*));
            memset(ses, 0, strlen(ses));
            memcpy(ses, "work in progress", 17);
            return ses;
        }

    }
    return "No suitable reply";

    }

    int main(int argc, char **argv){

    if(argc < 2){
        printf("Usage: ./server port");
        exit(0);
    }

    int serverfd;
    serverfd = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in server;
    server.sin_port = htons(atoi(argv[1]));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;

    if(bind(serverfd, (struct sockaddr *)&server, sizeof(server)) < 0){
        perror("Bind() error: ");
        fflush(stderr);
    }

    //"UDP message receiver" variables declarations
    int bytes; //Reads how many bytes the funcion recvfrom has read
    struct sockaddr_in from;

    char* buffer = malloc(sizeof(char*)); //String to which save the client message
    memset(buffer, 0, strlen(buffer)); //and set it to zero

    socklen_t fromlen = sizeof(struct sockaddr_in);

    const char stdrep[] = "Message Received: "; //This string will always be
                                                //printed upon receiving a message
    char* reply = malloc(sizeof(char*)); //This is where the return value of
                                        //stdreply() will be stored
    memset(reply, 0, strlen(reply)); //and set it zero

    //This while will keep "listening" for udp messages
    while((bytes = recvfrom(serverfd, buffer, 1024, 0, (struct sockaddr *)&from, &fromlen)) > 0){
        //From teacher's example. Write to stdout
        write(1, stdrep, strlen(stdrep));
        write(1, buffer, bytes);
        //Detect a basically empty string (if the client has pressed only enter)
        if(buffer[0] == '\n'){
            bytes = sendto(serverfd, "You pressed only enter!\n", 18, 0, (struct sockaddr *)&from, fromlen);
        }

        //Act according to the client message
        reply = stdreply(buffer);
        bytes = sendto(serverfd, reply, strlen(reply), 0, (struct sockaddr *)&from, fromlen);
        if (bytes  < 0){
            perror("sendto: ");
            fflush(stderr);
        }
        memset(buffer, 0, 1024);
        memset(reply, 0, strlen(reply)); //The seg fault happens right here
        fflush(stdout);
    }
    return 0;
}

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018

В коде, который вы публикуете, много проблем.

  1. Как уже отмечалось @JonBolinger, sizeof(char*) возвращает размер в байтах указателя на символ.На платформах Intel это будет 4 или 8, в зависимости от того, работаете ли вы на 32-разрядной или 64-разрядной версии.(Таким образом, вы в конечном итоге выделяете буферы размером 4 или 8 байтов)

  2. Вы последовательно пытаетесь очистить свои динамически распределенные буферы с помощью memset().malloc() вернет память, заполненную мусором, и вы укажете, сколько байтов очистить, используя strlen() в возвращенном буфере.strlen() будет сканировать буфер до тех пор, пока не найдет первые 0 символов для вычисления длины строки. Поскольку буфер заполнен мусором, это может легко дать вам значение вне границ вашего блока памяти, и вы в конечном итоге повредите память.

  3. Каждыйвызов malloc() должен соответствовать вызову free(), иначе вы потеряете память.Это особенно важно, если ваша программа работает долго.

Когда вы работаете с временными локальными строками (строками, которые не возвращаются вызывающей стороне), очень распространенной практикой является использование локального массива char вместо malloc ().Таким образом, буфер выделяется в стеке и будет автоматически освобожден, когда ваша функция выйдет из области видимости.Обязательно используйте «безопасные» строковые функции, такие как strncpy(), которые будут принимать в качестве параметра длину буфера, чтобы избежать перезаписи.

void Example(char* anotherString ) {
    char tmpString[256];   // this will create a local buffer with capacity of 256 bytes
    strncpy(tmpString, anotherString, sizeof(tmpString));  // copy string, without risk of overflowing the buffer
}

Предупреждение: НИКОГДА попытатьсяверните локальный временный буфер в результате, помните, что он больше не будет существовать при выходе из функции, и хотя возвращаемое значение может изначально иметь значимые результаты, они обязательно будут уничтожены, как только вы вызовете другую функцию.Вместо этого, другая распространенная практика, когда вам нужно возвращать строковое значение вместо того, чтобы возвращать строку, выделенную с помощью malloc(), которая должна быть освобождена с помощью free() - вы передаете локальный буфер, который будет содержать результат как параметрвот так:

void func1() {
    char result[256];
    func2(result, 256);
    // after calling, result will carry "a returned string"
}

void func2(char* result, size_t bufferLen) {
    strncpy(result, "a returned string", bufferLen);
}

Я думаю, что ваш код очень выиграл бы, если бы вы могли преобразовать его в этот стиль, где это применимо.

0 голосов
/ 23 ноября 2018
reply = stdreply(buffer);

Это не копирует строку.Он перезаписывает указатель другим, теряя исходный указатель.

memset(reply, 0, strlen(reply));

Это очистит строку, если она была выделена с помощью malloc.Если это постоянная строка типа «Нет подходящего ответа», то она может быть доступна только для чтения, поэтому она генерирует ошибку segfault.

...