Proxy源代码分析--谈谈如何学习linux网络编程.doc
《Proxy源代码分析--谈谈如何学习linux网络编程.doc》由会员分享,可在线阅读,更多相关《Proxy源代码分析--谈谈如何学习linux网络编程.doc(31页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到,Linux和Windows这样的傻瓜操作系统(这里丝毫没有贬低Windows的意思,相反这应该是Windows的优点)相比,后者无疑在易操作性上更胜一筹。但是为什么又有那么多的爱好者钟情于Linux呢,当然自由是最吸引人的一点,另外Linux强大的功能也是一个非常重要的原因,尤其是Linux强大的网络功能更是引人注目。放眼今天的WAP业务、银行网络业务和曾经红透半边天的电子商务,都越来越倚重基于Linux的解决方案。因此Linux网络编程是非常重要的,而且当我们一接触到Linux网络编程,我们就会发现这是一件非常有
2、意思的事情,因为以前一些关于网络通信概念似是而非的地方,在这一段段代码面前马上就豁然开朗了。在刚开始学习编程的时候总是让人感觉有点理不清头绪,不过只要多读几段代码,很快我们就能体会到其中的乐趣了。下面我就从一段Proxy源代码开始,谈谈如何进行Linux网络编程。 首先声明,这段源代码不是我编写的,让我们感谢这位名叫Carl Harris的大虾,是他编写了这段代码并将其散播到网上供大家学习讨论。这段代码虽然只是描述了最简单的proxy操作,但它的确是经典,它不仅清晰地描述了客户机/服务器系统的概念,而且几乎包括了Linux网络编程的方方面面,非常适合Linux网络编程的初学者学习。这段Prox
3、y程序的用法是这样的,我们可以使用这个proxy登录其它主机的服务端口。假如编译后生成了名为Proxy的可执行文件,那么命令及其参数的描述为:./Proxy 其中参数proxy_port是指由我们指定的代理服务器端口。参数remote_host是指我们希望连接的远程主机的主机名,IP地址也同样有效。这个主机名在网络上应该是唯一的,如果您不确定的话,可以在远程主机上使用uname -n命令查看一下。参数service_port是远程主机可提供的服务名,也可直接键入服务对应的端口号。这个命令的相应操作是将代理服务器的proxy_port端口绑定到remote_host的service_port端口
4、。然后我们就可以通过代理服务器的proxy_port端口访问remote_host了。例如一台计算机,网络主机名是legends,IP地址为10.10.8.221,如果在我的计算机上执行:rootlee /root#./proxy 8000 legends telnet那么我们就可以通过下面这条命令访问legends的telnet端口。-rootlee /root#telnet legends 8000Trying 10.10.8.221.Connected to legends(10.10.8.221).Escape character is Red Hat Linux release 6.
5、2(Zoot)Kernel 2.2.14-5.0 on an i686Login:-上面的绑定操作也可以使用下面的命令:rootlee /root#./proxy 8000 10.10.8.221 2323是telnet服务的标准端口号,其它服务的对应端口号我们可以在/etc/services中查看。下面我就从这段代码出发谈谈我对Linux网络编程的一些粗浅的认识,不对的地方还请各位大虾多多批评指正。main()函数-#include #include #include #include #include #include #include #include #include #include
6、 #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
7、servfd); void do_proxy (int usersockfd); void reap_status (void); void errorout (char *msg);/*This is my modification. Ill 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 con
8、nections 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; in
9、t 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
10、 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); /
11、* 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(sockf
12、d, (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 (childpi
13、d = 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;原因
14、是在我的Linux环境下头文件stdio.h已经对sys_errlist进行了如下定义:extern _const char *_const sys_errlist;也许Carl Harris在94年编写这段代码时系统还没有定义sys_errlist,不过现在我们不修改一下的话,编译时系统就会告诉我们sys_errlist发生了定义冲突。另外我添加了一个函数类型定义:typedef void Sigfunc(int);具体原因我将在后面向大家解释。套接字和套接字地址结构定义这段主程序是一段典型的服务器程序。网络通讯最重要的就是套接字的使用,在程序的一开始就对套接字描述符sockfd和newso
15、ckfd进行了定义。接下来定义客户机/服务器的套接字地址结构cliaddr和servaddr,存储客户机/服务器的有关通信信息。然后调用parse_args(argc,argv)函数处理命令参数。关于这个parse_args()函数我们待会儿再做介绍。创建通信套接字下面就是建立一个服务器的详细过程。服务器程序的第一个操作是创建一个套接字。这是通过调用函数socket()来实现的。socket()函数的具体描述为:-#include #include int socket(int domain, int type, int protocol);-参数domain指定套接字使用的协议族,AF_IN
16、ET表示使用TCP/IP协议族,AF_UNIX表示使用Unix协议族,AF_ISO表示套接字使用ISO协议族。type指定套接字类型,一般的面向连接通信类型(如TCP)设置为SOCK_STREAM,当套接字为数据报类型时,type应设置为SOCK_DGRAM,如果是可以直接访问IP协议的原始套接字则type应设置为SOCK_RAW。参数protocol一般设置为0,表示使用默认协议。当socket()函数成功执行时,返回一个标志这个套接字的描述符,如果出错则返回-1,并设置errno为相应的错误类型。设置服务器套接字地址结构在通常情况下,首先要将描述服务器信息的套接字地址结构清零,然后在地址结
17、构中填入相应的内容,准备接受客户机送来的连接建立请求。这个清零操作可以用多种字节处理函数来实现,例如bzero()、bcopy()、memset()、memcpy()等,以字母b开始的两个函数是和BSD系统兼容的,而后面两个是ANSI C提供的函数。这段代码中使用的bzero()其描述为:void bzero(void *s, int n);函数的具体操作是将参数s指定的内存的前n个字节清零。memset()同样也很常用,其描述为:void *memset(void *s, int c, size_t n);具体操作是将参数s指定的内存区域的前n个字节设置为参数c的内容。下一步就是在已经清零的
18、服务器套接字地址结构中填入相应的内容。Linux系统的套接字是一个通用的网络编程接口,它应该支持多种网络通信协议,每一种协议都使用专门为自己定义的套接字地址结构(例如TCP/IP网络的套接字地址结构就是struct sockaddr_in)。不过为了保持套接字函数调用参数的一致性,Linux系统还定义了一种通用的套接字地址结构:-struct sockaddrunsigned short sa_family; /* address type */char sa_data14; /* protocol address */-其中sa_family意指套接字使用的协议族地址类型,对于我们的TCP/
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Proxy 源代码 分析 谈谈 如何 学习 linux 网络 编程
限制150内