免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3855 | 回复: 6
打印 上一主题 下一主题

HTTP代理系统的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-07-13 15:35 |只看该作者 |倒序浏览
本帖最后由 ohyeahbbs 于 2011-07-13 15:38 编辑

小弟跟着老师在弄一个HTTP代理系统;捣鼓了一个星期终于容易可以代理成功了,却发现html的页面只能打开一两个,然后用top查看Linux系统资源发现代理占据资源达到了七八十%。
哪位大虾帮忙看看,优化一下。。。
采用线程绑定文件描述字的方法。。。。
代码如下:
  1. 1,proxy:

  2. #include "threadpool/include/threadpool.hpp"
  3. #include<iostream>
  4. #include<string.h>// char *strerror(int errnum);errnum(通常就是errno)
  5. #include<sys/socket.h>
  6. #include<errno.h>
  7. #include<arpa/inet.h>
  8. #include<unistd.h>
  9. #include<signal.h>
  10. #include<stdlib.h>

  11. #define BACKLOG 128

  12. using namespace std;

  13. int         httpproxy(void *fd);

  14. int        listenfd;                //文件(套接字socket)描述符
  15. volatile bool        gIsRunning;        //volatile表示把gIsRunning添加到内存中

  16. boost::threadpool::pool threadPool; //利用线程池创建线程并设线程大小为66

  17. /* 关闭proxy */
  18. void sysclosed(int signal)   
  19. {
  20.         gIsRunning = false;
  21.         cout<<"Waiting for proxy threads..."<<endl;
  22.             threadPool.wait();
  23.         close(listenfd);
  24.         cout<<"proxy is closed"<<endl;
  25. }

  26. int main()
  27. {
  28.         int                        *connfd;        //accept所返回的文件(套接字)描述符                       
  29.         socklen_t                client;
  30.         struct sockaddr_in        cliaddr,servaddr;
  31.         gIsRunning = true;

  32.         system("service iptables restart");
  33.         sleep(3);

  34.         /* 端口80重点向为本地端口9997 */
  35.           system("iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 9997");
  36.         cout<<"iptables prerouting success"<<endl;

  37.         /* 创立并监听socket */
  38.         cout<<"Start the server..."<<endl;
  39.         if((listenfd = socket(AF_INET, SOCK_STREAM, 0))<0 )
  40.         {
  41.                 cout<<"Creat the socket error!"<<endl;
  42.                 exit(-1);       
  43.         }
  44.         else cout<<"Creat the socket success"<<endl;
  45.        
  46.         memset(&servaddr,0,sizeof(servaddr));
  47.         servaddr.sin_addr.s_addr  = INADDR_ANY;
  48.         servaddr.sin_family            = AF_INET;
  49.         servaddr.sin_port        = htons(9997);
  50.                
  51.         /* 绑定任意IP及特定端口 */
  52.         if(bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) <0 )
  53.         {
  54.                 cout<<"Bind error"<<strerror(errno)<<endl;
  55.         }
  56.         else cout<<"Bind the port success"<<endl;

  57.         /* 在socket上监听连接 */
  58.         if(listen(listenfd, BACKLOG) != 0 )
  59.         {
  60.                 cout<<"Listen error"<<strerror(errno)<<endl;
  61.         }
  62.         else cout<<"Listen is running..."<<endl;
  63.        
  64.         threadPool.size_controller().resize(66);
  65.           
  66.         signal(SIGINT, sysclosed);

  67.         /* 循环接受TCP连接 */       
  68.         for( ; gIsRunning == 1 ; )
  69.         //while(gIsRunning)
  70.         {
  71.                 client = sizeof(cliaddr);
  72.                 connfd = (int*)malloc(sizeof(int));

  73.                 /* accept所返回的文件描述符是新的套接字描述符和原始套接字listenfd具有相同的套接字类型和地址簇
  74.                  * 传给accept的原始套接字没有关联到这个连接,而是继续保持可用状态并接受其他连接请求 */
  75.                 if((*connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &client))<0)
  76.                 {
  77.                
  78.                         /*accetp()是慢系统调用,在信号产生时会中断其调用并将
  79.                          errno变量设置为EINTR,此时应重新调用accept()。*/
  80.                         if( errno == EINTR )
  81.                         {
  82.                                 cout<<"Accept error (EINTR):"<<strerror(errno)<<endl;
  83.                                 continue;
  84.                         }
  85.                                else
  86.                                {
  87.                                 cout<<"Accept error:"<<strerror(errno)<<endl;
  88.                                 exit(1);
  89.                         }
  90.                 }
  91.                 else cout<<"Start the http connect!"<<endl;

  92.                 /* 把accept返回的套接字描述符与线程httpproxy绑定 */
  93.                 threadPool.schedule(boost::bind(&httpproxy, (void *)connfd));

  94.         }
  95. }


  96. 2,forwarddata:

  97.                 #include<sys/select.h>
  98. #include<iostream>
  99. #include<string.h>
  100. #include<sys/socket.h>
  101. #include<errno.h>
  102. #include<arpa/inet.h>
  103. #include <linux/kernel.h>
  104. #include <linux/netfilter_ipv4.h>

  105. #define MAXLINE 4096  

  106. /* 声明外部变量gIsRunning */
  107. extern volatile bool gIsRunning;

  108. using namespace std;

  109. int forwarddata(int connfd, int remotefd);

  110. int max(int a, int b)
  111. {
  112.         return a>b?a:b;
  113. }

  114. int httpproxy(void *fd)
  115. {
  116.         int                 connfd,remotefd,port;
  117.         socklen_t        len;
  118.         char                buff[100+1];
  119.         const char *        ipaddr;

  120.         struct sockaddr_in        remoteserv;
  121.         struct sockaddr_in        dest;

  122.         connfd = *((int *)fd);
  123.         free(fd);

  124.         /* 获取连接的目的IP和PORT */
  125.         memset(buff, 0x00, sizeof(buff));
  126.         len =sizeof(dest);
  127.         memset(&dest, 0, len);
  128.         if(getsockopt(connfd, SOL_IP, SO_ORIGINAL_DST, &dest, &len)< 0)
  129.         {
  130.                 cout<<"Getsockopt error:"<<strerror(errno)<<endl;
  131.                 return -1;
  132.         }
  133.         else
  134.         {
  135.                 ipaddr = inet_ntop(AF_INET, &dest.sin_addr, buff, sizeof(buff));
  136.                 port   = ntohs(dest.sin_port);
  137.                 cout<<"Iptables transparent destination:"<<ipaddr<<":"<<port<<endl;
  138.         }

  139.         cout<<"buff="<<buff<<" "<<"port="<<port<<endl;

  140.         /* 创建到服务器socket */
  141.         if((remotefd = socket(AF_INET, SOCK_STREAM, 0))<0)
  142.         {
  143.                 cout<<"Remote error:"<<strerror(errno)<<endl;
  144.                 return -1;
  145.         }
  146.         memset(&remoteserv, 0x00, sizeof(remoteserv));
  147.         remoteserv.sin_family = AF_INET;
  148.         remoteserv.sin_port   = htons(ntohs(dest.sin_port));
  149.         inet_pton(AF_INET, buff, &remoteserv.sin_addr);
  150.        
  151.         /* 按目的地址建立连接 */
  152.         if(connect(remotefd, (struct sockaddr *)&remoteserv, sizeof(remoteserv))<0)
  153.         {
  154.                 cout<<"Connect remote server error:"<<strerror(errno)<<endl;
  155.                 //goto STOP_THREAD;
  156.                 return -1;
  157.         }
  158.         else
  159.         {       
  160.                 /* 处理connfd和remotefd的数据交换 */
  161.                 forwarddata(connfd, remotefd);
  162.         }
  163.         return 0;
  164. }

  165. int forwarddata(int connfd, int remotefd)
  166. {
  167.         int        maxfdp1;
  168.         int        ret, n;
  169.         char        buff[MAXLINE];
  170.         fd_set        rset;                 //分配一个状态描述符类型的变量
  171.         struct timeval val;
  172.         val.tv_sec = 10;
  173.         val.tv_usec = 0;

  174.         FD_ZERO(&rset);                //将一个指定的fd_set变量的所有位设置为0.
  175.         ret = 0;
  176.        
  177.         /* 循环地从客户端读,向服务端写;从服务器端读,向客户端写 */
  178.         for( ; gIsRunning==1 ; )
  179.         //printf("test3=%d\n", gIsRunning);
  180.         //while(gIsRunning)
  181.         {
  182.                 /* 调用FD_SET设置一个fd_set变量的指定位。添加描述符到描述符集合*/
  183.                 FD_SET(connfd, &rset);
  184.                 FD_SET(remotefd, &rset);
  185.        
  186.                 /* 因为描述符编号从0开始,所以要在最大描述符编号值上加1 */
  187.                 maxfdp1 = max(connfd, remotefd) + 1;
  188.                
  189.                 /* 返还值-1表示出错 */
  190.                 if((n = select(maxfdp1, &rset, NULL, NULL, &val))<0)
  191.                 {
  192.                         cout<<"Select serror!"<<endl;
  193.                         break;
  194.                 }
  195.                 /* 判断是否为超时返回 */
  196.                 if( n == 0 )
  197.                         continue;

  198.                 /* 从客户端读recv(client) */
  199.                 if(FD_ISSET(connfd, &rset)) //测试一指定位是否设置,检查FD集合中FD是否准备好
  200.                 {
  201.                         memset(buff, 0x00, sizeof(buff));
  202.                         ret = recv(connfd, buff, MAXLINE, 0);
  203.                         if(ret > 0)
  204.                         {
  205.                                 cout<<"Client to server..."<<endl;

  206.                                 /* 向服务器端写send(server) */
  207.                                 ret = send(remotefd, buff, ret, 0);
  208.                                 if(ret == -1)
  209.                                 {
  210.                                         cout<<"Send data to real server error"<<endl;
  211.                                         break;
  212.                                 }
  213.                         }
  214.                         /* 没读到数据关闭连接 */
  215.                         else if(ret == 0)
  216.                         {
  217.                                 break;
  218.                         }
  219.                         /* 从客户端接受数据错误 */
  220.                         else
  221.                         {
  222.                                 break;
  223.                         }
  224.                 }

  225.                 /* 从服务器端读recv(server) */
  226.                 if(FD_ISSET(remotefd, &rset))
  227.                 {
  228.                         memset(buff, 0x00, sizeof(buff));
  229.                         ret = recv(remotefd, buff, MAXLINE, 0);
  230.                         if(ret > 0)
  231.                         {
  232.                                 cout<<"Remote to client..."<<endl;

  233.                                 /* 向客户端写send(client) */
  234.                                 ret = send(connfd, buff, ret, 0);
  235.                                 if(ret == -1)
  236.                                 {
  237.                                         cout<<"Send data to client error"<<endl;
  238.                                         break;
  239.                                 }
  240.                         }
  241.                         /* 没读到数据关闭连接 */
  242.                         else if(ret == 0)
  243.                         {
  244.                                 break;
  245.                         }
  246.                         /* 从客户端接受数据错误 */
  247.                         else
  248.                         {
  249.                                 break;
  250.                         }
  251.                 }

  252.         }
  253.         return 0;

  254. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-07-13 15:39 |只看该作者
坐等高人··指点····

论坛徽章:
0
3 [报告]
发表于 2011-07-13 16:04 |只看该作者

论坛徽章:
0
4 [报告]
发表于 2011-07-13 16:08 |只看该作者
val.tv_sec = 10;
val.tv_usec = 0;
放到select函数前面就成。。。

论坛徽章:
0
5 [报告]
发表于 2011-07-13 17:29 |只看该作者
val.tv_sec = 10;
val.tv_usec = 0;
放到select函数前面就成。。。
xiboboy123 发表于 2011-07-13 16:08


嗯嗯,top  中的pproxy代理占用资源的确有明显大量的减少,可以html代理连接的页面依然只能代理2~3个页面,点击多个时打不开·········

论坛徽章:
0
6 [报告]
发表于 2011-07-13 17:45 |只看该作者
是不是线程绑定的资源没有释放导致不能打开多个页面的链接吖。。。。??

如果后面我不采用线程来绑定文件秒数字来处理调用httpproxy函数的调用,
而采用进程来调用就不会产生不能打开多个页面的问题。。。这是为什么呢???
进程替换线程代码如下:
要替换的:
  1.       /* 把accept返回的套接字描述符与线程httpproxy绑定 */
  2.                 threadPool.schedule(boost::bind(&httpproxy, (void *)connfd));
复制代码
替换为:
  1. /* 创建子进程,处理请求 */
  2.                 if((pid = fork()) > 0)
  3.                 {
  4.                         /* 父进程处理 */
  5.                         free(connfd);
  6.                         continue ;
  7.                 }
  8.                 else if(pid == 0)
  9.                 {
  10.                         /* 子进程处理 */
  11.                         close(listenfd);
  12.                         cout<<"Start the http connect!"<<endl;
  13.                         httpproxy((void *)connfd);
  14.                         exit(0);
  15.                 }
  16.                 else if(pid < 0)
  17.                 {
  18.                         cout<<"Creat the child process fail! "<<endl;
  19.                         exit(2);
  20.                 }
  21.                 close(listenfd);
复制代码
这样就可以打开多个网页了······不懂真不懂这是为啥????线程不是用完自动释放,重复利用的麽??
望大虾指点·····

论坛徽章:
0
7 [报告]
发表于 2011-07-14 09:55 |只看该作者
是不是由这个原因啊???有木有大虾啊??
由于程序用select等待连接完成,可以设置一个select等待时间限制,从而缩短connect超时时间。多数实现中,connect的超时时间在75秒到几分钟之间。有时程序希望在等待一定时间内结束,使用非阻塞connect可以防止阻塞75秒,在多线程网络编程中,尤其必要。   例如有一个通过建立线程与其他主机进行socket通信的应用程序,如果建立的线程使用阻塞connect与远程通信,当有几百个线程并发的时候,由于网络延迟而全部阻塞,阻塞的线程不会释放系统的资源,同一时刻阻塞线程超过一定数量时候,系统就不再允许建立新的线程(每个进程由于进程空间的原因能产生的线程有限),如果使用非阻塞的connect,连接失败使用select等待很短时间,如果还没有连接后,线程立刻结束释放资源,防止大量线程阻塞而使程序崩溃。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP