Proxy源代码分析 谈Linux收集编程身手

  来历:网海拾贝  




Linux是一个可靠性特别很是高的把持系统,但是扫数效过Linux的友人都市觉获得,Linux和Windows多么的"傻瓜"把持系统(这里丝毫没有抬高Windows的意思,相反这应该是Windows的利益)比拟,后者无疑在易把持性上更胜一筹。但是为什么又有那么多的乐趣者钟情于Linux呢,当然自由是最吸惹人的一点,别的Linux强盛的功用也是一个特别很是次要的缘由,尤其是Linux强盛的收集功用更是惹人瞩目。放眼今天的WAP营业、银行收集营业和已经红透半边天的电子商务,都越来越倚重基于Linux的处置方案。因此Linux收集编程是特别很是次要的,而且当我们一构兵到Linux收集编程,我们就会发现这是一件特别很是有心思的事变,因为畴昔一些关于收集通信观念貌同实异的处所,在这一段段代码面前即速就豁然沉闷了。在刚下手着手学习编程的时分总是让人以为有点理不清眉目,不过只需多读几段代码,很快我们就能体会到其中的乐趣了。下面我就从一段Proxy源代码下手着手,谈谈如何休止Linux收集编程。

首先声明,这段源代码不是我编写的,让我们谢谢这位名叫Carl Harris的大年夜虾,是他编写了这段代码并将其散播到网上供大家学习商洽。这段代码固然只是刻画了最复杂的proxy把持,但它的确是经典,它不但清楚地刻画了客户机/效劳器系统的观念,而且的确包含了Linux收集编程的方方面面,特别很是适合Linux收集编程的初学者学习。

这段Proxy法式的用法是多么的,我们可以运用这个proxy登录别的主机的效劳端口。假定编译后生成了名为Proxy的可实行文件,那么号令及其参数的刻画为:

./Proxy

其中参数proxy_port是指由我们指定的代庖署理效劳器端口。参数remote_host是指我们停留衔接的长途主机的主机名,IP地点也异常无效。这个主机名在收集上应该是独一的,假定您不确定的话,可以在长途主机上运用uname -n号令查察一下。参数service_port是长途主机可供给的效劳名,也可直接键入效劳对应的端口号。这个号令的相应把持是将代庖署理效劳器的proxy_port端口绑定到remote_host的service_port端口。然后我们就可以经由代庖署理效劳器的proxy_port端口拜访remote_host了。譬喻一台较量争论机,收集主机名是legends,IP地点为10.10.8.221,假定在我的较量争论机上实行:

[root@lee /root]#./proxy 8000 legends telnet

那么我们就可以经由下面这条号令拜访legends的telnet端口。

  [root@lee /root]#telnet legends 8000
  Trying 10.10.8.221...
  Connected to legends(10.10.8.221).
  Escape character is '^]'
  Red Hat Linux release 6.2(Zoot)
  Kernel 2.2.14-5.0 on an i686
  Login:

下面的绑定把持也可以运用下面的号令:

[root@lee /root]#./proxy 8000 10.10.8.221 23

23是telnet效劳的榜样楷模端口号,别的效劳的对应端口号我们可以在/etc/services中查察。

下面我就从这段代码动身谈谈我对Linux收集编程的一些深刻的熟悉,舛错的处所还请各位大年夜虾多多批评匡正。

◆main()函数
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #define TCP_PROTO   "tcp" 
  int proxy_port;    /* port to listen for proxy connections on */ 
struct sockaddr_in hostaddr;   /* host addr assembled from gethostbyname() */ 
  extern int errno;   /* defined by libc.a */ 
  extern char *sys_myerrlist[]; 
  void parse_args (int argc, char **argv); 
  void daemonize (int servfd); 
  void do_proxy (int usersockfd); 
  void reap_status (void); 
  void errorout (char *msg);
  /*This is my modification. 
  I'll tell you why we must do this later*/
  typedef void Signal(int);
  /指点指点指点指点指点指点指点指点指点指点****
  function:    main 
  description:
Main level driver. After daemonizing the process,
 a socket is opened to listen for connections on the proxy port,
 connections are accepted and children are spawned to
handle each new connection. 
  arguments:    argc,argv you know what those are. 
  return value:  none. 
  calls:      parse_args, do_proxy. 
  globals:     reads proxy_port. 
  指点指点指点指点指点指点指点指点指点指点****/
  main (argc,argv) 
  int argc; 
  char **argv; 
  { 
     int clilen; 
     int childpid; 
     int sockfd, newsockfd; 
     struct sockaddr_in servaddr, cliaddr; 
     parse_args(argc,argv); 
     /* prepare an address struct to listen for connections */ 
     bzero((char *) &servaddr, sizeof(servaddr)); 
     servaddr.sin_family = AF_INET; 
     servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
     servaddr.sin_port = proxy_port; 
     /* get a socket... */ 
     if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { 
       fputs("failed to create server socketrn",stderr); 
       exit(1); 
     } 
     /* ...and bind our address and port to it */ 
     if   (bind(sockfd,(struct sockaddr_in *) &servaddr,sizeof(servaddr)) < 0) { 
       fputs("faild to bind server socket to specified portrn",stderr); 
       exit(1); 
      } 
     /* get ready to accept with at most 5 clients waiting to connect */ 
     listen(sockfd,5); 
    /* turn ourselves into a daemon */ 
    daemonize(sockfd); 
    /* fall into a loop to accept new connections and spawn children */ 
    while (1) {
      /* accept the next connection */ 
      clilen = sizeof(cliaddr); 
      newsockfd = accept(sockfd, (struct sockaddr_in *) &cliaddr, &clilen); 
      if (newsockfd < 0 && errno == EINTR) 
        continue; 
      /* a signal might interrupt our accept() call */ 
      else if (newsockfd < 0) 
        /* something quite amiss -- kill the server */ 
      errorout("failed to accept connection");
      /* fork a child to handle this connection */ 
      if ((childpid = fork()) == 0) { 
        close(sockfd); 
        do_proxy(newsockfd); 
        exit(0); 
       } 
      /* if fork() failed, the connection is silently dropped -- oops! */ 
       lose(newsockfd); 
       } 
     }

下面便是Proxy源代码的主法式部分,大年夜概您在网上也已经看到过这段代码,不过仔细的您会发现在下面这段代码中我批改了两个处所,都是在预编译部分。一个处所是在定义内部字符型指针数组时,我将原代码中的

extern char *sys_errlist[];

批改为

extern char *sys_myerrlist[];缘由是在我的Linux景象下头文件"stdio.h"已经对sys_errlist[]休止了如下定义:

extern __const char *__const sys_errlist[];

大年夜概Carl Harris在94年编写这段代码时系统还没有定义sys_errlist[],不过现在我们不批改一下的话,编译时系统就会告诉我们sys_errlist发生了定义争辩。

别的我添加了一个函数榜样楷模定义:

typedef void Sigfunc(int);

详细缘由我将在背面向大家评释。

套接字和套接字地点机关定义

这段主法式是一段典型的效劳器法式。收集通信最次要的便是套接字的运用,在法式的一下手着手就对套接字刻画符sockfd和newsockfd休止了定义。接上去定义客户机/效劳器的套接字地点机关cliaddr和servaddr,存储客户机/效劳器的有关通信信息。然后挪用parse_args(argc,argv)函数措置赏罚号令参数。关于这个parse_args()函数我们待会儿再做引见。

树立通信套接字

下面便是设立设置装备摆设一个效劳器的详细进程。效劳器法式的第一个把持是树立一个套接字。这是经由挪用函数socket()来完成的。socket()函数的详细刻画为:

#include

#include

int soc




版权声明: 原创作品,容许转载,转载时请务必以超链接情势标明文章 原始情由 、作者信息和本声明。不然将清查法令责任。

posted @ 2011-03-07 20:36  蓝色的天空III  阅读(333)  评论(0)    收藏  举报