Может ли сервер использовать тот же сокет для отправки ответа клиенту? как?

Я использую сокеты Berkeley (оба: домен Интернета и домен Unix), и мне было интересно, могут ли сервер использовать те же сокеты для чтения запроса и для написания ответа клиенту. Или клиент должен создать другой сокет, чтобы дождаться повтора и сервер подключиться к нему после обработки полученного сообщения.

Кстати, я говорю о сокетов, ориентированных на соединение (потоковые сокеты, TCP,...).

Это упрощенный код сервера (я просто пропустил проверку ошибок на системных вызовах только для простоты):

int main() {

    int server_socket, connected_socket;
    struct sockaddr_in server_addr;
    char buf[1024];
    char aux[256];
    int bytes_read;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);    

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(1234);
    bind(server_socket, &server_addr, sizeof(server_addr))

    listen(server_socket, 5)

    connected_sodket = accept(server_socket, 0, 0);
    do {
        bzero(buf, sizeof(buf));
        bytes_read = read(connected_socket, buf, sizeof(buf));        
    } while (bytes_read > 0);          

    /* Here I want to use connected_socket to write the reply, can I? */

    close(connected_socket);       

    close(server_socket);

    return (EXIT_SUCCESS);
}

И это упрощенный клиентский код (здесь я просто обманываю проверку ошибок на системных вызовах):

int main() {

    int client_socket;
    struct sockaddr_in server_addr;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    hp = gethostbyname("myhost");
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
    server_addr.sin_port = htons(1234);

    connect(client_socket, &server_addr, sizeof(server_addr));

    write(client_socket, MSG, sizeof(MSG));

    /* Here I want to wait for a response from the server using client_socket, can I? */

    close(client_socket);

    return (EXIT_SUCCESS);
}

Могу ли я использовать connected_socket на сервере и client_socket в клиенте для передачи ответного сообщения? Или я должен использовать адрес клиента, который я получаю на сервере, когда "принимать" для подключения к сокету на клиенте?

Я попытался использовать read/wrint в клиенте/сервере, где отображается комментарий, но таким образом обе программы блокируются, кажется, что это блокировка.

Спасибо, вперед! С наилучшими пожеланиями.

Ответ 1

Вы можете использовать один и тот же сокет, но ваша программа настроена так, чтобы сервер читал ВСЕ, что клиент отправляет, прежде чем пытаться ответить. Таким образом, цикл на сервере не будет завершен до тех пор, пока клиент не закроет сторону записи своего сокета, поэтому сервер получит EOF (чтение в 0 байта), и, следовательно, сервер никогда не отправит ответ.

Есть несколько способов, которыми вы можете справиться с этим.

  • Вы можете разбить цикл на сервере после просмотра всего запроса, а не читать до EOF. Это требует, чтобы данные, отправленные клиентом, были как-то саморазграничивающимися, поэтому сервер может знать, когда он его прочитал.
  • Вы можете использовать второе соединение для ответа. Наверное, не самый лучший.
  • Вы можете использовать асимметричное выключение сокета. Попросите клиента выполнить shutdown(client_socket, SHUT_WR), чтобы закрыть его. Затем сервер увидит EOF (и цикл завершится), но другое направление в сокете по-прежнему будет открыто для ответа.

Ответ 2

Вы должны использовать тот же сокет!

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


  • Клиент устанавливает соединение с сервером;
  • клиент отправляет свой запрос (с помощью send());
  • клиент знает, в соответствии с протоколом, что сервер ответит; поэтому он ожидает данных в одном сокете (recv());
  • после проверки ответа клиент может закрыть сокет.

  • Сервер принимает соединение с клиентом;
  • сервер знает, что первый шаг зависит от клиента, поэтому он ожидает данных (recv());
  • сервер проверяет запрос;
  • сервер теперь знает, из протокола, что клиент ждет данных; следовательно, он отправляет свой ответ с помощью send();
  • сервер знает, из протокола, что дальнейших шагов нет; следовательно, он может закрыть сокет.

Ответ 3

Да, это возможно. Посмотрите эту страницу для примера простого сервера (и простого клиента). Обратите внимание, что сервер обычно передает дескриптор "accept" ed file в новый процесс, чтобы продолжить прослушивание дополнительных входящих соединений.

Ответ 4

Не только вы должны использовать один и тот же сокет (как говорит Федерико), вам действительно нужно, чтобы получить ваши обратные пакеты через брандмауэры.

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

Ответ 5

Да, SOCK_STREAM сокеты двухсторонние. Вы должны иметь возможность читать и писать в/из одного и того же сокета на каждой стороне соединения. Страница руководства для socket(2) содержит более подробную информацию об этом.

Ответ 6

Да, вы можете. Сокеты TCP двунаправлены. Просто используйте те же функции read() и write(). Также убедитесь, что вы проверяете наличие ошибок во всех вызовах connect(), read(), write(),... поскольку вы не можете контролировать, что происходит в сети.

Ответ 7

К сожалению, Я не сказал этого, но я действительно пробовал это, как это

Этот код на сервере, где есть комментарий:

write(connected_socket, "Ok, I got it", sizeof("Ok, I got it"));

и этот код в клиенте, где комментарий:

read(client_socket, buf, sizeof(buf));

Обе программы блокируются, и когда я убиваю клиента, сервер показывает полученные сообщения (у меня есть printf сразу после того, как сервер вызывает читать).

Я пытаюсь отправить send и recv (оба с флагами 0) вместо чтения и записи, и он не изменился.

Ответ 8

В вашей текущей настройке сервер пытается прочитать, пока клиент не закроет сокет, в то время как клиент не закрывает сокет до тех пор, пока сервер не ответит. Поэтому у вас есть своего рода "тупик". Сервер не останавливается, чтобы читать в надежде, что еще несколько данных могут появиться, и у клиента нет способа сообщить серверу, что он сделан.

Вам нужно, чтобы сервер узнал, что запрос завершен, а соединение все еще открыто. Если вы, например, прекратите свой запрос с помощью новой строки, сервер может проверить, получен ли он полную строку, а затем прекратить чтение и отправить ответ.

Ответ 9

Вы пробовали? Похоже, что он должен работать, когда вы на самом деле помещаете код там, где указано