如何创建一个简单的代理来访问C中的Web服务器

时间:2010-12-30 20:00:43

标签: c sockets proxy webproxy

我正在尝试在C中创建一个小型Web代理。首先,我正在尝试获取一个网页,将GET帧发送到服务器。 我不知道我错过了什么,但我没有收到任何回复。如果您能帮助我找到此代码中缺少的内容,我将非常感激。

int main (int argc, char** argv) {
   int cache_size,     //size of the cache in KiB
       port,
       port_google = 80,
       dir,
       mySocket,
       socket_google;

   char google[] = "www.google.es", ip[16];
   struct sockaddr_in socketAddr;
   char buffer[10000000];

   if (GetParameters(argc,argv,&cache_size,&port) != 0)
       return -1;

   GetIP (google, ip);
   printf("ip2 = %s\n",ip);

   dir = inet_addr (ip);
   printf("ip3 = %i\n",dir);

   /* Creation of a socket with Google */
   socket_google = conectClient (port_google, dir, &socketAddr);
   if (socket_google < 0) return -1;
   else printf("Socket created\n");

   sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
   if (write(socket_google, (void*)buffer, MESSAGE_LENGTH+1) < 0 )
       return 1;
   else printf("GET frame sent\n");

   strcpy(buffer,"\n");
   read(socket_google, buffer, sizeof(buffer));

   // strcpy(message,buffer);
   printf("%s\n", buffer);

   return 0;
}

这是我用来创建套接字的代码。我认为这部分是可以的,但我复制它以防万一。

int conectClient (int puerto, int direccion, struct sockaddr_in *socketAddr) {
   int mySocket;
   char error[1000];

   if ( (mySocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
       printf("Error when creating the socket\n");
       return -2;
   }

   socketAddr->sin_family = AF_INET;
   socketAddr->sin_addr.s_addr = direccion;
   socketAddr->sin_port = htons(puerto);

   if (connect (mySocket, (struct sockaddr *)socketAddr,sizeof (*socketAddr)) == -1) {
       snprintf(error, sizeof(error), "Error in %s:%d\n", __FILE__, __LINE__);
       perror(error);
       printf("%s\n",error);
       printf ("-- Error when stablishing a connection\n");
       return -1;
   }
   return mySocket;
}

谢谢!

3 个答案:

答案 0 :(得分:2)

首先,您没有检查write(2)调用实际写入套接字的字节数。调用的返回值告诉您。 read(2)也是如此。 TCP套接字是一个双向流,因此规则总是在循环中执行,直到传输预期的字节数,读取EOF(从read(2)返回零)或发生错误(在阅读时你没有检查。)

然后HTTP是相当复杂的协议。熟悉RFC 2616,尤其是应用程序级别的连接管理和传输编码。

编辑0:

嗯,没有“简单”代理这样的东西。您需要管理多个连接(至少是客户端到代理和代理到服务器),因此最好调查select(2) / poll(2) / epoll(4) / {{3系统调用系列,允许您kqueue(2) I / O.这通常与非阻塞套接字结合使用。查看辅助库,如multiplex。看看在libevent这样的优秀网络服务器/代理中如何做到这一点。听起来很像你发现的很多,但不要担心,这很有趣:)

答案 1 :(得分:1)

由于您没有发布GetIP例程,我不确定您的主机名查找是否正确,因为从它的外观来看,我不确定您是否正确使用inet_addr函数

尼古拉指出了一些非常好的观点(我完全同意)。事实上,您GET请求实际上已被破坏,而当我在我自己的系统上的本地Apache Web服务器上测试它时,它无效。

sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
if (write(socket_google, (void*)buffer, LONGITUD_MSJ+1) < 0 )
    return 1;
else printf("GET frame sent\n");
...

strcpy(buffer,"\n");
read(socket_google, buffer, sizeof(buffer));

应替换为

  snprintf(buffer, sizeof(buffer), 
      "GET / HTTP/1.1\r\nHost: %s\r\nUser-Agent: TEST 0.1\r\n\r\n", 
      google);

  if (write(socket_google, buffer, strlen(buffer)+1) < 0 ) {
      close(socket_google);
      return 1;
  } else 
      printf("GET frame sent\n");
  ...

  buffer[0] = '\0';
  /* Read message from socket */
  bytes_recv = read(socket_google, buffer, sizeof(buffer));
  if (bytes_recv < 0) {
       fprintf(stderr, "socket read error: %s\n", strerror(errno));
       close(socket_google);
       exit(10);
  }

  buffer[bytes_recv] = '\0';    /* NUL character */

  /* strcpy(message,buffer); */
  printf("%s\n", buffer);

  ...

在退出程序之前,您还应该close套接字。启用编译器的标准C89 / 90或C99模式(例如gcc的-std=c99)并启用警告(例如gcc的-Wall)并读取它们。 #include函数原型的必要头文件(在我的例子中假设为Linux):

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>      /* for gethostbyname() */

对于主机名/ IP地址解析,有一些强制转换指针和struct,这可能会让人感到困惑和容易犯错,所以验证是否有效正如你所期望的那样。

 in_addr_t ip;
 ...

 GetIP(google, &ip);   /* I changed the parameters */
 printf("IP address = %x (%s)\n", 
     ip, 
     inet_ntoa(*((struct in_addr*)&ip)));

答案 2 :(得分:1)

实际上,我一直在使用名为rzsocket link to it的库实现一个小型Web代理。

我在实现Web代理时发现的最困难的事情之一,也许这可能也是您的问题,是的,为了使代理正常工作,我不得不将keep-alive设置为false。在FireFox中执行此操作的一种方法是访问 about:config 地址,并将network.http.proxy.keep-alive的值设置为false