Как прочитать ответ из сокета в c? - PullRequest
1 голос
/ 17 сентября 2010

Я пытаюсь написать TCP-клиент, который выполняет следующие действия:

1. Establish TCP connection to webserver
2. Accept GET request command from user's console
3. Client should get a reply back from webserver after each GET request.

У меня трудное время для третьего условия.Я не получил никакого ответа от веб-сервера.

Вот мой код:

    s = connectTCP(host, service);
 while (fgets(buf, sizeof(buf), stdin)) {
   buf[LINELEN-2]='\r';    /* ensure catridge return   */
   buf[LINELEN-1]='\n'; /* ensure line feed return  */
   buf[LINELEN] = '\0'; /* ensure line null-terminated */
   outchars = strlen(buf);
   (void) write(s, buf, outchars);
   printf("Start reading from socket...\n");
   fflush(stdout);
   while( (n = read(s, buf, LINELEN)) > 0) {
    buf[n] = '\0';  /* ensure null-terminated */
    (void) fputs( buf, stdout );
    fflush(stdout);
   }
 }

Ответы [ 3 ]

1 голос
/ 17 сентября 2010

HTTP-запрос заканчивается с двумя последовательностями возврата каретки-перевода строки ("\ r \ n \ r \ n"). То, как вы создаете свой запрос, даже не гарантирует его наличия. Может быть, вместо этого что-то вроде:

while (fgets(buf, sizeof(buf) - 3, stdin)) {
    size_t outsz = strlen(buf);
    if (outsz > 0 && buf[outsz - 1] == '\n')
        outsz--;
    strcpy(buf + outsz, "\r\n\r\n");
    write(s, buf, outsz + 4);
0 голосов
/ 17 сентября 2010

В этом коде есть несколько открытых лазеек, которые могут привести к проблеме. Действительно ставлю на пункт 2.

1 - действительно ли connectTCP работает? Действительно ли он вернул работающий TCP-сокет. Вы можете легко закрыть эту проверку на наличие ошибок при вызове write() вместо того, чтобы приводить их к void.

2 - если LINELEN является некоторой константой, такой как размер массива, размещение \r\n в конце массива не очень поможет, если между пользовательским вводом и концом массива есть мусор (шансы вероятны есть некоторые 0). sizeof(buf) - это максимально допустимое значение, не обязательно равное размеру того, что будет вводить пользователь.

3 - double \r\n не обязательно, достаточно одного '\ n' (соглашение Unix).

4 - использование интерактивного пользовательского ввода для тестирования, как правило, не очень хорошая идея, это затрудняет отладку и тестирование кода.

5 - остерегайтесь записи в позиции n в вашем буфере, если буфер чтения заполнен, он может находиться за концом буфера.

5 - если вы хотите больше, чем просто отправить одну команду и получить ответ, вам придется написать более сложный код, который управляет как write (), так и read (). Как предложил другой автор, хорошая идея - выбрать (вероятно, проще, чем нить). Вы также можете блокировать более чередующиеся команды, отправленные и полученные, но все это, вероятно, относится к другим вопросам.

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

#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/un.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

enum {
    LINELEN = 1024
};

int main(){
    char buf[LINELEN];
    int n = 0;
    unsigned int option_len;
    int allow_reuse = 1;
    int sck = socket(PF_INET, SOCK_STREAM, 0);
    char * ip = "www.google.com";
    char * command = "GET http://www.google.com\n";

    /* connect to socket */
    struct sockaddr_in s;
    memset(&s, 0, sizeof(struct sockaddr_in));
    s.sin_family = AF_INET;
    s.sin_port = htons(80);
    s.sin_addr.s_addr = inet_addr(ip);
    if (s.sin_addr.s_addr == INADDR_NONE) {
        struct hostent *h = gethostbyname(ip);
        if (!h) {
            printf("DNS resolution failed for %s\n", ip);
            exit(0);
        }
        s.sin_addr.s_addr = *((int*)(*(h->h_addr_list)));
    }
    connect(sck, (struct sockaddr*)&s, sizeof(s));

    /* write command */
    printf("Write to socket...\n");
    write(sck, command, strlen(command));

    /* get answer */
    printf("Start reading from socket...\n");
    while((n = read(sck, buf, LINELEN-1)) > 0) {
       buf[n] = '\0';  /* ensure null-terminated */
       fputs( buf, stdout );
    }
}
0 голосов
/ 17 сентября 2010

Какие розетки вы используете?Если вы используете (windows) сокеты Berkley, вы можете использовать select для проверки данных и recv / recvfrom для получения данных

...