⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 proxy源代码分析 谈linux网络编程技术(1)_梦里香巴拉的空间.htm

📁 一个经典的网络代理程序和讲解
💻 HTM
📖 第 1 页 / 共 5 页
字号:
#ft A:visited {
	LETTER-SPACING: normal
}
</STYLE>

<DIV id=usrbar><NOBR><A id=hi_index href="http://hi.baidu.com/" 
target=_blank>百度空间</A>&nbsp;|&nbsp;<A href="http://www.baidu.com/" 
target=_blank>百度首页</A>&nbsp;
<SCRIPT type=text/javascript>		document.write('| <a href="https://passport.baidu.com/?login&tpl=sp&tpl_reg=sp&u=http://hi.baidu.com' + encodeURIComponent('/%C3%CE%C0%EF%CF%E3%B0%CD%C0%AD/blog/item/6441782c84d571ed8a1399fb%2Ehtml') + '">登录</a>');		</SCRIPT>
 </NOBR></DIV>
<DIV id=newUserTip 
style="Z-INDEX: 30000; RIGHT: 0px; FLOAT: right; WIDTH: 225px; POSITION: absolute; TOP: 26px; HEIGHT: 130px; TEXT-ALIGN: right"><A 
id=newUserTipShadow 
style="DISPLAY: block; FONT-SIZE: 14px; Z-INDEX: 30001; RIGHT: 14px; BACKGROUND: none transparent scroll repeat 0% 0%; OVERFLOW: hidden; WIDTH: 55px; COLOR: #4242f9! important; POSITION: absolute; TOP: 108px; HEIGHT: 14px; TEXT-DECORATION: none! important" 
onclick=newUserRegLog() href="http://hi.baidu.com/st/reg.html" 
target=_blank>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</A> 
<EMBED id=newUserTipSwf style="WIDTH: 225px; HEIGHT: 130px" 
src=http://hi.baidu.com/ui/flash/userReg/guide.swf 
type=application/x-shockwave-flash wmode="transparent" 
allowScriptAcess="always"> </DIV>
<SCRIPT language=javascript>	<!--		function newUserTipShow(f)		{			if(f=="0"){//close				G("newUserTipSwf").style.width="40px";				G("newUserTip").style.width="40px";				G("newUserTipShadow").style.display="none";			}else{//show				G("newUserTip").style.width="225px";				G("newUserTipSwf").style.width="225px";				G("newUserTipShadow").style.display="block";			}		}		function newUserRegLog(){			var now=new Date();			now.setTime(now.getTime()+5*60*1000);			document.cookie="BDSP_REGFLAG=1;expires="+now.toGMTString()+";path=/";			new Image().src="http://hi.baidu.com/sys/statlog/1.gif?m=blog_newer_pro2_click&v=/%C3%CE%C0%EF%CF%E3%B0%CD%C0%AD&t="+Math.random();		}	//-->	</SCRIPT>

<SCRIPT type=text/javascript>function set_cookie_4_bdtip(index/* start from one */, value){	var bdtip = document.cookie.match(/(^| )BDTIP=([^;]*)(;|$)/);	if(!bdtip){		bdtip=new Array(index);		for(var i=0,n=bdtip.length;i<n;i++)		{			if(bdtip[i]=="" || bdtip[i]==null)  bdtip[i]=0;			if(i == index - 1){				bdtip[i] = value;			}		}	}else{	bdtip = bdtip[2].split('-');	if(index > bdtip.length)  bdtip.length= index;	for(var i = 0, j = bdtip.length; i < j; i ++){		if(bdtip[i]=="" || bdtip[i]==null) bdtip[i]=0;		if(i == index - 1){			bdtip[i] = value;		}	}	}	bdtip = bdtip.join('-');	document.cookie = "BDTIP=" + bdtip+ ";expires=Wed, 28-Nov-37 01:45:46 GMT;path=/;";}</SCRIPT>

<DIV id=main align=left><!--[if IE]>
<SCRIPT>
var objmain = document.getElementById("main");
function updatesize(){ var bodyw = window.document.body.offsetWidth; if(bodyw <= 790) objmain.style.width="772px"; else if(bodyw >= 1016) objmain.style.width="996px"; else objmain.style.width="100%"; }
updatesize(); window.onresize = updatesize;
</SCRIPT>
<![endif]-->
<DIV id=header>
<DIV class=lc>
<DIV class=rc></DIV></DIV>
<DIV class=tit><A class=titlink title="梦里香巴拉的空间 http://hi.baidu.com/梦里香巴拉" 
href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;">梦里香巴拉的空间</A></DIV>
<DIV class=desc></DIV>
<DIV id=tabline></DIV>
<DIV id=tab><A href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;">主页</A><A class=on 
href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;/blog">博客</A><A 
href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;/album">相册</A><SPAN>|</SPAN><A 
href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;/profile">个人档案</A> <SPAN>|</SPAN><A 
href="http://hi.baidu.com/&Atilde;&Icirc;&Agrave;&iuml;&Iuml;&atilde;°&Iacute;&Agrave;&shy;/friends">好友</A> </DIV></DIV>
<DIV class=stage>
<DIV class=stagepad>
<DIV style="WIDTH: 100%">
<TABLE class=modth cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD class=modtl width=7>&nbsp;</TD>
    <TD class=modtc noWrap>
      <DIV class=modhead><SPAN class=modtit>查看文章</SPAN></DIV></TD>
    <TD class=modtc noWrap align=right></TD>
    <TD class=modtr width=7>&nbsp;</TD></TR></TBODY></TABLE>
<DIV class=modbox id=m_blog>
<DIV class=tit>[转]Proxy源代码分析 谈Linux网络编程技术(1)</DIV>
<DIV class=date>2007-06-03 13:07</DIV>
<TABLE style="TABLE-LAYOUT: fixed">
  <TBODY>
  <TR>
    <TD>
      <DIV class=cnt id=blog_text>
      <P>首先声明,这段源代码不是我编写的,让我们感谢这位名叫Carl 
      Harris的大虾,是他编写了这段代码并将其散播到网上供大家学习讨论。这段代码虽然只是描述了最简单的proxy操作,但它的确是经典,它不仅清晰地描述了客户机/服务器系统的概念,而且几乎包括了Linux网络编程的方方面面,非常适合Linux网络编程的初学者学习。<BR>  这段Proxy程序的用法是这样的,我们可以使用这个<FONT 
      color=#0000ff>proxy登录其它主机的服务端口。</FONT>假如编译后生成了名为Proxy的可执行文件,那么命令及其参数的描述为:<BR>   ./Proxy 
      &lt;proxy_port&gt; &lt;remote_host&gt; 
      &lt;service_port&gt;<BR>  其中参数proxy_port是指由我们指定的代理服务器端口。参数remote_host是指我们<FONT 
      color=#0000ff>希望连接的远程主机的主机名,</FONT>IP地址也同样有效。这个主机名在网络上应该是唯一的,如果您不确定的话,可以在远程主机上使用uname 
      -n命令查看一下。参数<FONT 
      color=#0000ff>service_port是远程主机可提供的服务名,也可直接键入服务对应的端口号</FONT>。这个命令的相应操作是<FONT 
      color=#0000ff>将代理服务器的proxy_port端口绑定到remote_host的service_port端口</FONT>。然后我们就可以通过<FONT 
      color=#0000ff>代理服务器</FONT>的proxy_port端口访问remote_host了。例如一台计算机,网络主机名是legends,IP地址为10.10.8.221,如果在我的计算机上执行:<BR>   [root@lee 
      /root]#./proxy 8000 legends telnet<BR>  那么我们就可以通过下面这条命令访问<FONT 
      color=#0000ff>legends的telnet端</FONT>口。<BR>-----------------------------------------------------------------<BR>[root@lee 
      /root]#telnet legends 8000<BR>Trying 10.10.8.221...<BR>Connected to 
      legends(10.10.8.221).<BR>Escape character is '^]'</P>
      <P>Red Hat Linux release 6.2(Zoot)<BR>Kernel 2.2.14-5.0 on an 
      i686<BR>Login:<BR>-----------------------------------------------------------------<BR>  上面的绑定操作也可以使用下面的命令:<BR>   [root@lee 
      /root]#./proxy 8000 10.10.8.221 23<BR>   <FONT 
      color=#0000ff>23是telnet服务的标准端口号,其它服务的对应端口号我们可以在/etc/services中查看。</FONT></P>
      <P>  下面我就从这段代码出发谈谈我对Linux网络编程的一些粗浅的认识,不对的地方还请各位大虾多多批评指正。</P>
      <P>◆main()函数<BR>-----------------------------------------------------------------<BR>#include 
      &lt;stdio.h&gt; <BR>#include &lt;ctype.h&gt; <BR>#include &lt;errno.h&gt; 
      <BR>#include &lt;signal.h&gt; <BR>#include &lt;sys/types.h&gt; 
      <BR>#include &lt;sys/socket.h&gt; <BR>#include &lt;sys/file.h&gt; 
      <BR>#include &lt;sys/ioctl.h&gt; <BR>#include &lt;sys/wait.h&gt; 
      <BR>#include &lt;sys/types.h&gt; <BR>#include &lt;netdb.h&gt; <BR>#define 
      TCP_PROTO   "tcp" <BR>int proxy_port;    /* port to listen for proxy 
      connections on */ <BR>struct sockaddr_in hostaddr;   /* host addr 
      assembled from gethostbyname() */ <BR>extern int errno;   /* defined by 
      libc.a */ <BR>extern char *sys_myerrlist[]; <BR>void parse_args (int argc, 
      char **argv); <BR>void daemonize (int servfd); <BR>void do_proxy (int 
      usersockfd); <BR>void reap_status (void); <BR>void errorout (char 
      *msg);<BR>/*This is my modification. <BR>I'll tell you why we must do this 
      later*/<BR>typedef void 
      Signal(int);<BR>/****************************************************************<BR>function: 
         main <BR>description:   Main level driver. After daemonizing the 
      process, a socket is opened to listen for         </P>
      <P>connections on the proxy port, connections are accepted and children 
      are spawned to         handle each new</P>
      <P>connection. <BR>arguments:    argc,argv you know what those are. 
      <BR>return value:  none. <BR>calls:      parse_args, do_proxy. 
      <BR>globals:     reads proxy_port. 
      <BR>****************************************************************/<BR>main 
      (argc,argv) <BR>int argc; <BR>char **argv; <BR>{ <BR>   int clilen; 
      <BR>   int childpid; <BR>   int sockfd, newsockfd; <BR>   struct 
      sockaddr_in servaddr, cliaddr; <BR>   parse_args(argc,argv); <BR>   /* 
      prepare an address struct to listen for connections */ <BR>   bzero((char 
      *) &amp;servaddr, sizeof(servaddr)); <BR>   servaddr.sin_family = AF_INET; 
      <BR>   servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
      <BR>   servaddr.sin_port = proxy_port; <BR>   /* get a socket... */ 
      <BR>   if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) &lt; 0) { 
      <BR>     fputs("failed to create server socket\r\n",stderr); 
      <BR>     exit(1); <BR>   } <BR>   /* ...and bind our address and port to 
      it */ <BR>   if   (bind(sockfd,(struct sockaddr_in *) 
      &amp;servaddr,sizeof(servaddr)) &lt; 0) { <BR>     fputs("faild to bind 
      server socket to specified port\r\n",stderr); <BR>     exit(1); <BR>    } 
      <BR>   /* get ready to accept with at most 5 clients waiting to connect */ 
      <BR>   listen(sockfd,5); <BR>  /* turn ourselves into a daemon */ 
      <BR>  daemonize(sockfd); <BR>  /* fall into a loop to accept new 
      connections and spawn children */ <BR>  while (1) {<BR>    /* accept the 
      next connection */ <BR>    clilen = sizeof(cliaddr); <BR>    newsockfd = 
      accept(sockfd, (struct sockaddr_in *) &amp;cliaddr, &amp;clilen); 
      <BR>    if (newsockfd &lt; 0 &amp;&amp; errno == EINTR) 
      <BR>      continue; <BR>    /* a signal might interrupt our accept() call 
      */ <BR>    else if (newsockfd &lt; 0) <BR>      /* something quite amiss 
      -- kill the server */ <BR>    errorout("failed to accept 
      connection");<BR>    /* fork a child to handle this connection */ 
      <BR>    if ((childpid = fork()) == 0) { <BR>      close(sockfd); 
      <BR>      do_proxy(newsockfd); <BR>      exit(0); <BR>     } <BR>    /* if 
      fork() failed, the connection is silently dropped -- oops! */ 
      <BR>     lose(newsockfd); <BR>     } 
      <BR>   }<BR>-----------------------------------------------------------------<BR>  上面就是Proxy源代码的主程序部分,也许您在网上也曾经看到过这段代码,不过细心的您会发现在上面这段代码中我修改了两个地方,都是在预编译部分。一个地方是在定义外部字符型指针数组时,我将原代码中的  <FONT 
      color=#0000ff>extern char *sys_errlist[];<BR></FONT>修改为<BR>  extern char 
      *sys_myerrlist[];原因是在我的Linux环境下头文件"stdio.h"已经对sys_errlist[]进行了如下定义:<BR>  extern 
      __const char *__const sys_errlist[];<BR>  也许Carl 
      Harris在94年编写这段代码时系统还没有定义sys_errlist[],不过现在我们不修改一下的话,编译时系统就会告诉我们sys_errlist发生了定义冲突。<BR>  另外我添加了一个函数类型定义:<BR>   typedef 
      void Sigfunc(int);<BR>  具体原因我将在后面向大家解释。</P>
      <P><FONT color=#0000ff>套接字和套接字地址结构定义</FONT></P>
      <P>  这段主程序是一段典型的服务器程序。网络通讯最重要的就是套接字的使用,在程序的一开始就对套接字描述符<FONT 
      color=#0000ff>sockfd和newsockfd</FONT>进行了定义。接下来定义客户机/服务器的套接字地址结构<FONT 
      color=#0000ff>cliaddr和servaddr</FONT>,存储客户机/服务器的有关通信信息。然后调用parse_args(argc,argv)函数处理命令参数。关于这个parse_args()函数我们待会儿再做介绍。</P>
      <P>创建通信套接字</P>
      <P>  下面就是建立一个服务器的详细过程。服务器程序的第一个操作是创建一个套接字。这是通过调用函数socket()来实现的。</P>
      <P>socket()函数的具体描述为:<BR>-----------------------------------------------------------------<BR>  #include 
      &lt;sys/types.h&gt;<BR>  #include &lt;sys/socket.h&gt;<BR>  int socket(int 
      domain, int type, int 
      protocol);<BR>-----------------------------------------------------------------<BR>  参数<FONT 
      color=#0000ff>domain指定套接字使用的协议族,AF_INET表示使用TCP/IP协议族</FONT>,AF_UNIX表示使用Unix协议族,AF_ISO表示套接字使用ISO协议族。type指定套接字类型,一<FONT 
      color=#0000ff>般的面向连接通信类型(如TCP)设置为SOCK_STREAM,</FONT>当套接字为数据报类型时,type应设置为SOCK_DGRAM,如果是可以直接访问IP协议的原始套接字则type应设置为SOCK_RAW。参数<FONT 
      color=#0000ff>protocol一般设置为"0",</FONT>表示使用默认协议。当socket()函数成功执行时,返回一个标志这个套接字的描述符,如果出错则返回"-1",并设置errno为相应的错误类型。</P>
      <P>设置服务器套接字地址结构</P>
      <P>  在通常情况下,首先要将描述服务器信息的套接字地址结构清零,然后在地址结构中填入相应的内容,准备接受<FONT 
      color=#0000ff>客户机送来的连接建立请求。这个清零操作可以用多种字节处理函数来实现,例</FONT>如bzero()、bcopy()、memset()、memcpy()等,以字母"b"开始的两个函数是和BSD系统兼容的,而后面两个是ANSI 
      C提供的函数。这段代码中使用的bzero()其描述为:<BR>   void bzero(void *s, int 
      n);<BR>  函数的具体操作是将参数s指定的内存的前n个字节清零。memset()同样也很常用,其描述为:<BR>   void 
      *memset(void *s, int c, size_t 
      n);<BR>  具体操作是将参数s指定的内存区域的前n个字节设置为参数c的内容。<BR>  下一步就是在已经清零的服务器套接字地址结构中填入相应的内容。Linux系统的套接字是一个通用的网络编程接口,它应该支持多种网络通信协议,每一种协议都使用专门为自己定义的套接字地址结构(例如<FONT 
      color=#0000ff>TCP/IP网络的套接字地址结构就是structsockaddr_in</FONT>)。不过为了保持套接字函数调用参数的一致性,Linux系统还定义了一种通用的套接字地址结构:<BR>-----------------------------------------------------------------<BR>&lt;linux/socket.h&gt;<BR>struct 
      sockaddr<BR>{<BR>  unsigned short sa_family; /* address type */<BR>  char 
      sa_data[14]; /* protocol address 
      */<BR>}<BR>-----------------------------------------------------------------<BR>  其中sa_family意指套接字使用的协议族地址类型,对于我们的TCP/IP网络,其值应该是AF_INET,sa_data中存储具体的协议地址,不同的协议族有不同的地址格式。这个通用的套接字地址结构一般不用做定义具体的实例,但是常用做套接字地址结构的强制类型转换,如我们经常可以看到这样的用法:<BR>   bind(sockfd,(struct 
      sockaddr *) &amp;servaddr,sizeof(servaddr))<BR>  用于TCP/IP协议族的套接字地址结构是<FONT 
      color=#0000ff>sockaddr_in</FONT>,其定义为:<BR>-----------------------------------------------------------------<BR>&lt;linux/in.h&gt;<BR>struct 
      in_addr<BR>{<BR>  __u32 s_addr;<BR>};<BR>  struct 
      sochaddr_in<BR>{<BR>  short int sin_family;<BR>  unsigned short int 
      sin_port;<BR>  struct in_addr sin_addr;<BR>   /*This part has not been 
      taken into use yet*/<BR>   nsigned char_ _ pad[_ _ SOCK_SIZE__- 
      sizeof(short int) -sizeof(unsigned short int) -       sizeof(struct</P>
      <P>in_addr)];<BR>};<BR>#define sin_zero_ - 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -