2010-09-17 20 views
1

我试图写一个TCP客户端,做以下的事情:如何阅读c中的套接字答复?

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); 
    } 
} 

回答

1

的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

它没有伤害,但实际上并不需要大多数服务器(甚至可以使用单个'\ n'而不是'\ r \ n')。 – kriss 2010-09-17 11:26:09

+0

@caf'fgets()'只填充数据的'size - 1'字符,并且总是附加一个空值,所以'-3'很好。 'write'包含要发送的数据的长度,所以字符串不需要以NUL结尾。 'strcpy()'(也许''strcat()')虽然可能更好。 – llasram 2010-09-17 12:01:58

+0

同意不需要'write()'的终止符 - 可以使用'memcpy()'代替。 – caf 2010-09-17 12:11:24

0

您正在使用什么插座?如果您使用(窗口)Berkeley套接字可以使用select测试数据和recv/recvfrom获取数据

+0

我在Ubuntu中使用berkley套接字。如何使用select? – root 2010-09-17 10:49:41

+0

我很喜欢Beej的网络指南:http://beej.us/guide/bgnet/ – raven 2010-09-17 11:03:26

+0

是的,这是一个很好的指导,尤其是对于linux/unix用户:) – Necrolis 2010-09-17 11:13:42

0

有这个代码,可能导致问题的几种开环孔。真的我敢打赌第二点。

1- does connectTCP really really works?它实际上是否返回了可用的TCP套接字。致电write()时,您可以轻松关闭该错误以检查错误,而不是将它们投射至无效状态。

2-如果LINELEN与数组的大小相似,将\r\n放在数组的末尾不会对用户输入和数组末尾之间的垃圾有很大帮助(机会可能在那里是一些0)。 sizeof(buf)是最大合法值,不一定是用户输入的大小。

3-双\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); 
    } 
}