Клиент (программа python) не получает ответ обратно с сервера (c программой)? - PullRequest
0 голосов
/ 22 января 2019

У меня есть приложение с сокетом UDP, где я работаю на стороне сервера.Для тестирования серверной части я собрал простую клиентскую программу на Python, которая отправляет сообщение «Привет, мир, как дела».Затем сервер должен получить сообщение, преобразовать его в верхний регистр и отправить обратно клиенту.Проблема заключается в следующем: я могу наблюдать во время отладки, что сервер получает сообщение, применяет преобразование, отправляет ответ обратно и в конечном итоге ожидает другое сообщение.Однако клиент Python не получает сообщение, а бесконечно ждет ответа от сервера.

Я нашел (вариант) через Интернет, что для того, чтобы клиент получил ответ обратно, ему необходимо привязаться ксервер, который идет вразрез с тем, что я видел в учебнике (Интерфейс программирования Linux).Тем не менее, я попытался привязать клиента к серверу, и программе python не удалось подключиться к линии привязки (не знаю, правильно ли я это сделал).Версия Python 2.7.5.Клиентская программа работает на RedHat, а сервер работает на целевом модуле с Angstrom (он кросс-скомпилирован для 32-разрядного процессора).

Вот код для клиента:

import socket
import os

UDP_IP = "192.168.10.4"
UDP_PORT = 50005

#dir_path = os.path.dirname(os.path.realpath(__file__))

MESSAGE = "hello world how are you"

print "UDP target IP: ", UDP_IP
print "UDP target port: ", UDP_PORT
print "message: ", MESSAGE

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#sock.bind((UDP_IP, UDP_PORT))
print "Sending message..."
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
print "Message sent!"
print "Waiting for response..."
data = sock.recv(1024)

print "Received", repr(data)

И вот код для сервера:

void server_side(void)
{
    printf("Server start up.\n");

    struct sockaddr_in svaddr;
    struct sockaddr_in claddr;
    int sfd;
    int j;
    ssize_t numBytes;
    socklen_t len;
    char buf[BUF_SIZE];
    char claddrStr[INET_ADDRSTRLEN];

    //int output = open("test_output.txt", O_WRONLY|O_CREAT, 0664);

    printf("Creating new UDP socket...\n");

    sfd = socket(AF_INET, SOCK_DGRAM, 0);   /* Create Server Socket*/
    if (sfd == -1)
    {
        errExit("socket");
    }

    printf("Socket has been created!\n");

    memset(&svaddr, 0, sizeof(struct sockaddr_in));

    svaddr.sin_family = AF_INET;
    svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    svaddr.sin_port = htons(PORT_NUM);

    printf("Binding in process...\n");

    if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_in)) 
                == -1)
    {
        errExit("bind");
    }

    printf("Binded!\n");

    /* Receive messages, convert to upper case, and return to client.*/

    for(;;)
    {
        len = sizeof(struct sockaddr_in);
        numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
                (struct sockaddr *) &claddr, &len);
        if (numBytes == -1)
        {
            errExit("recvfrom");
        }
        if (inet_ntop(AF_INET, &claddr.sin_addr, claddrStr,
                INET_ADDRSTRLEN) == NULL)
        {
            printf("Couldn't convert client address to string.\n");
        }
        else
        {
            printf("Server received %ld bytes from (%s, %u).\n", (long)
                numBytes,
                    claddrStr, ntohs(claddr.sin_port));
        }

        claddr.sin_port = htons(PORT_NUM);

        for (j = 0; j< numBytes; j++)
        {
            buf[j] = toupper((unsigned char) buf[j]);
        }

        if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len)
               != numBytes)
        {
            fatal("sendto");
        }
    }
}

Опять проблема в том, что я не получаю ответ и не печатаю сообщение обратно на клиентском терминале.Я должен получить одно и то же сообщение заглавными буквами.Я чувствую, что мне не хватает маленькой детали.Спасибо за помощь!

1 Ответ

0 голосов
/ 22 января 2019

Быстро и грязно:

Удалите эту строку из кода C:

claddr.sin_port = htons(PORT_NUM);

Теперь почему:

Когда вы отправляете сообщение в своем скрипте Python, ваша операционная система заполнит UDP-пакет указанным вами IP-адресом назначения и портом, IP-адресом машины, которую вы используете в поле IP-адреса источника,и, наконец, порт источника, назначенный «случайным образом» ОС.Обратите внимание, что это не тот порт, который является портом назначения, и даже если бы это было так, как бы вы узнали, какая программа получит исходное сообщение (обе будут слушать сообщения с одного и того же порта) К счастью, это не таквозможно.

Теперь, когда ваш код C получает этот пакет, он будет знать, кто отправил сообщение, и у вас есть доступ к этой информации, хотя структура sockaddr заполнена recvfrom.Если вы хотите отправить некоторую информацию обратно, вы должны отправить пакет с портом назначения (который видит сервер), равным порту источника, который видит клиент, который, опять же, не тот порт, который вы слушаете насервер.Делая claddr.sin_port = htons(PORT_NUM), вы устанавливаете перезапись поля, содержащего исходный порт клиента, на порт сервера, и при попытке отправить этот пакет могут произойти 2 вещи:

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

ПолупеченныйАналогия: вы получаете письмо от друга, но когда пишете ему обратно, вы меняете номер его дома на номер вашего дома ... не имеет особого смысла.Разница лишь в том, что ваш друг много двигается, и каждая буква может иметь разное число, но это не важно.

Теоретически, вы должны связываться, если хотите получить данные обратно, в этом случаеbind - это эквивалент прослушивания этого порта.Этот ответ разъясняет, почему в этом случае это не было необходимо: https://stackoverflow.com/a/14243544/6253527

Если вы работаете в Linux, вы можете увидеть, какой порт ОС вы назначили для вашего сокета UDP, используя sudo ss -antup | grep python

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...