Как вы отправляете текущий рабочий каталог программ в C с помощью библиотеки winsock2?

Это мой код:

#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

int main(){
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serverAddr, clientAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(6969);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    listen(serverSocket, 1);

    int s_size = sizeof(struct sockaddr_in);
    SOCKET clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &s_size);

    char cwd[1024];
    getcwd(cwd, 1024);

    printf(cwd);

    send(clientSocket, cwd, sizeof(cwd), 0);

    return 0;
}

Он предназначен для отправки cwd программы в скрипт python, но всякий раз, когда я запускаю скрипт python, он выдает мне эту ошибку, когда получает cwd:

cwd = clientSocket.recv(1024).decode("utf-8").strip()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd4 in position 44: invalid continuation byte

Всякий раз, когда я печатаю cwd в программе C, он печатается без проблем. Я пробовал нулевое завершение строки следующим образом:

cwd[strlen(cwd)] = 0;

Это ничего не изменило.

Мое лучшее предположение заключается в том, что данные между фактическим путем и нулевым терминатором являются причиной ошибки кодирования в конце сценариев python, но я не могу выделить достаточно памяти для пути, потому что я не могу знать длину пути перед выделением памяти, поэтому я выбрал 1024, так как считал, что это разумное значение.

Кто-нибудь знает, как я могу это исправить?

РЕДАКТИРОВАТЬ: я решил это. В строке cwd было много мусора вместе с фактической строкой из-за большого размера буфера (1024). Вместо этого мне удалось выделить память для cwd и перераспределить ее на его strlen().

char *cwd = malloc(1024*sizeof(char));
getcwd(cwd, 1024);
realloc(cwd, strlen(cwd));

printf(cwd);

send(clientSocket, cwd, strlen(cwd), 0);

Это сработало для меня.


person Community    schedule 24.12.2019    source источник
comment
Вы отправляете полный буфер char, включая мусор после байта NUL. Попробуйте send(clientSocket, cwd, strlen(cwd) + 1, 0);   -  person Steve Friedl    schedule 24.12.2019
comment
Теперь это дает мне эту ошибку: ConnectionResetError: [Errno 104] Сброс соединения по узлу   -  person    schedule 24.12.2019
comment
Сброс соединения происходит из программы python (как я ожидаю) или из программы-отправителя на C?   -  person Steve Friedl    schedule 24.12.2019
comment
Вам следует закрыть clientSocket.   -  person Andrii    schedule 24.12.2019


Ответы (1)


Я не знаю Python, но думаю, что вижу, что здесь происходит: похоже, это несоответствие импеданса между двумя разными способами обработки строк. Это займет несколько шагов.

На стороне C это буфер символов:

char cwd[1024];
getcwd(cwd, sizeof cwd);
send(clientSocket, cwd, sizeof(cwd), 0);

а содержимое буфера cwd после строки текущего каталога будет представлять собой нулевой байт плюс случайный мусор из стека, и весь этот буфер отправляется по сети в программу Python.

Мое первоначальное предложение состояло в том, чтобы отправлять только те данные, которые действительны:

send(clientSocket, cwd, strlen(cwd), 0);  // don't do this after all

который не отправляет мусор, но теперь мы сталкиваемся с другой проблемой: принимающая сторона ожидает полных 1024 байта:

cwd = clientSocket.recv(1024).decode("utf-8").strip()

Проблема в том, что отправитель передает гораздо меньше байтов (длина /home/steve/myproject или что-то еще), и, поскольку сокет TCP не учитывает границы сообщения, он все еще пытается прочитать полные 1024.

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

Правильный способ исправить это, вероятно, включает в себя тайм-ауты и буферизацию на принимающей стороне, но это похоже на большую работу, поэтому я немного изменю направление.

Теперь мы вернемся к отправке буфера фиксированного размера (1024, как вы предлагаете), что означает, что обе стороны согласны с размером данных, но вам придется изменить сторону Python, чтобы извлечь только интересующую часть строки. , игнорируя байт NUL и все, что за ним.

На стороне Python вы все равно будете делать cwd = clientSocket.read(1024), но тогда вам придется выполнять манипуляции с буфером, чтобы узнать, где находится первый байт NUL, и использовать только до (но не включая) этого символа в буфере.

Результирующее подмножество (скажем, 47 байт) будет строкой, которую вы сможете декодировать и использовать.

Ваши результаты могут различаться в зависимости от того, как Python позволяет вам манипулировать буферами и преобразовывать их в строки; Я не знаю.

person Steve Friedl    schedule 24.12.2019
comment
Это звучит как возможное решение. Итак, в коде C, когда я добавляю нулевой терминатор в конец строки, он добавляет его в конец раздела памяти или только в конец допустимых символов? Я думал, что это добавит 0 в конце данных мусора, поэтому у меня будут фактические данные, данные мусора и нулевой терминатор в конце. - person ; 24.12.2019
comment
@dekotu - когда вы добавляете байт NUL, он помещается в одну позицию внутри массива символов, сразу после символа, о котором вы заботитесь (/home/steve/myproject/), но все, что следует за байтом NUL, неизвестно и, вероятно, является мусором. - person Steve Friedl; 24.12.2019
comment
Он не пытается прочитать полные 1024 символа. Он остановится на том, что было прочитано. - person user207421; 25.12.2019
comment
Мне удалось это решить. Я динамически выделил cwd, а затем перераспределил его в strlen(). - person ; 25.12.2019