📄 276.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>apue</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="192.htm">上一层</a>][<a href="277.htm">下一篇</a>]
<hr><p align="left"><small>发信人: guru (好读书,不求甚解), 信区: UNP <br>
标 题: 一个简易的proxy程序的开发过程(1) <br>
发信站: UNIX编程 (2001年10月03日21:02:48 星期三), 站内信件 <br>
<br>
:http://www.linuxaid.com.cn/developer/showdev.jsp?i=341 <br>
一个简易的proxy程序的开发过程(1) <br>
http://LinuxAid.com.cn 01-07-19 17:24 1445p ariesram <br>
---------------------------------------------------------------------------- <br>
---- <br>
一个简易的proxy程序的开发过程(1) <br>
1、引言 <br>
很多人都看过Eric Steven Raymond写的<<The Cathedral and the Bazaar>> (大教堂与 <br>
集市) 这篇文章吧。 <br>
这篇文章讲述了传统的开发小组开发方式和基于Internet的分散的开发方式(linux的开 <br>
发方式,GNU软件的 <br>
开发方式)的区别,并且根据自己的一个程序的开发例子来讲述了The Bazaar开发方式 <br>
的若干条重要原则。 <br>
不过,国内很多程序员,工作的时候还是采用的传统的开发方式,很难有机会在工作中 <br>
体验这些原则。那么, <br>
这个例子就给了大家又一个体验这些原则的过程。 <br>
这个例子,主要是运用了一些编程的技术,比如,socket编程,信号,进程,还有一些 <br>
unix socket编程的较 <br>
高级论题。当然,这些都不是主要的,重要的是,体验一下集市的开发方式。 <br>
2、开发这个proxy程序的背景 <br>
我工作的时候,处在一个比较封闭的网络环境中。我的机器在局域网 (LAN) 之中,与外 <br>
界的Internet相连采 <br>
用了代理的方式,有若干台unix服务器作为代理服务器,运行squid作为http的代理,运 <br>
行socks作为socks 5 <br>
代理。应该说,这样的待遇,还算不错,:-), 要浏览网站,squid够用了;要运行ICQ, <br>
OICQ之类的程序,用 <br>
socks也够了。但是,我遇到了一个比较麻烦的问题,在这样的网络环境中,我没有办法 <br>
用Outlook等工具收取 <br>
非来自公司邮件服务器的邮件(比方说,@linuxaid.com.cn, @163.net, @sina.com.cn <br>
等等);也没有办法用 <br>
Gravity等工具来收取USENET上的讨论。当然,折衷的办法还是有,我可以用linux下的 <br>
一些支持socks的邮件 <br>
客户端软件和新闻组阅读软件。但是,这样势必造成一些麻烦( 实际上我也这样做过 <br>
),当我需要收取邮件 <br>
或者阅读新闻组的时候,我必须重新启动机器转换到linux操作系统中去,而当我要办公 <br>
的时候,我又不得不 <br>
重新启动机器再转换到windows操作系统中来 ( 我不得不说,linux作为办公的桌面还是 <br>
不如windows, 虽然这 <br>
句话肯定会惹恼很多linux fan :-) )。作为一个程序员,我当然不能忍受这种麻烦。我 <br>
必须想办法来解决这 <br>
个问题。经过考虑,我有了一个好的想法。 <br>
这体现了The Bazaar原则一: <br>
Every good work of software starts by scratching a developer's personal itch <br>
. <br>
每一个软件的开发都是带有开发者自己的烙印。 <br>
3、初期设计 <br>
我需要的是一个程序,他能够做"二传手"的工作。他自身处在能同时连通外界目标服务 <br>
器和我的机器的位置上。 <br>
我的机器把请求发送给他,他接受请求,把请求原封不动的抄下来发送给外界目标服务 <br>
器;外界目标服务器 <br>
响应了请求,把回答发送给他,他再接受回答,把回答原封不动的抄下来发送给我的机 <br>
器。这样,我的机器 <br>
实际上是把他当作了目标服务器( 由于是原封不动的转抄,请求和回答没有被修改 ) <br>
。而他则是外界目标 <br>
服务器的客户端( 由于是原封不动的转抄,请求和回答没有被修改 )。我把这种代理 <br>
服务程序叫做"二传手"。 <br>
原理图如下: <br>
|--------------| |-----------------| |--------------------| <br>
| |------------------>| |---------------->| | <br>
| 我的机器 | | 代理服务程序 | | 目标服务器 | <br>
| |<------------------| |<----------------| | <br>
|--------------| |-----------------| |--------------------| <br>
4、例子重用 <br>
4、例子重用 <br>
The Bazaar原则二: <br>
Good programmers know what to write. Great ones know what to rewrite (and re <br>
use). <br>
好的程序员知道写什么。而伟大的程序员知道重写和重用什么。 <br>
基于这个原则,我当然不会从头来写这个程序(其实,这个程序是一个很小的程序,没 <br>
有必要一定要这么做。 <br>
但是,为了给大家,同时也是给我自己一个集市化的开发方式的体验,我还是这么做了 <br>
,我先是写出来了一个 <br>
简单的程序---附在本文最后----然后才想起来去找找有没有类似的程序 :-), 结果浪费 <br>
了很多时间)。 <br>
在网上找了找,花了大概半个小时( 和我写出第一个简单程序所花的时间差不多 :-) <br>
),找到了这个程序。 <br>
程序如下: <br>
---------------------------------------------------------------------------- <br>
-------------------- <br>
/*************************************************************************** <br>
* <br>
program: proxyd <br>
module: proxyd.c <br>
summary: provides proxy tcp service for a host on an isolated network. <br>
programmer: Carl Harris (ceharris@vt.edu) <br>
date: 22 Feb 94 <br>
description: <br>
This code implements a daemon process which listens for tcp connec- <br>
tions on a specified port number. When a connection is established, <br>
a child is forked to handle the new client. The child then estab- <br>
lishes a tcp connection to a port on the isolated host. The child <br>
then falls into a loop in which it writes data to the isolated host <br>
for the client and vice-versa. Once a child has been forked, the <br>
parent resumes listening for additional connections. <br>
The name of the isolated host and the port to serve as proxy for, <br>
as well as the port number the server listen on are specified as <br>
command line arguments. <br>
*************************************************************************** <br>
*/ <br>
#include <stdio.h> <br>
#include <ctype.h> <br>
#include <errno.h> <br>
#include <signal.h> <br>
#include <sys/types.h> <br>
#include <sys/socket.h> <br>
#include <sys/file.h> <br>
#include <sys/ioctl.h> <br>
#include <sys/wait.h> <br>
#include <netinet/in.h> <br>
#include <netdb.h> <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_errlist[]; /* defined by libc.a */ <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>
/*************************************************************************** <br>
* <br>
function: main <br>
description: Main level driver. After daemonizing the process, a socket <br>
is opened to listen for connections on the proxy port, <br>
connections are accepted and children are spawned to handle <br>
<br>
each new connection. <br>
arguments: <br>
argc,argv you know what those are. <br>
return value: none. <br>
calls: parse_args, do_proxy. <br>
globals: reads proxy_port. <br>
*************************************************************************** <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 *) &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)) < 0) { <br>
fputs("failed to create server socket <br>
",stderr); <br>
exit(1); <br>
} <br>
/* ...and bind our address and port to it */ <br>
if (bind(sockfd,(struct sockaddr_in *) &servaddr,sizeof(servaddr)) < 0) { <br>
fputs("faild to bind server socket to specified port <br>
",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 *) &cliaddr, &clilen); <br>
if (newsockfd < 0 && errno == EINTR) <br>
continue; /* a signal might interrupt our accept() call */ <br>
else if (newsockfd < 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>
close(newsockfd); <br>
} <br>
} <br>
/*************************************************************************** <br>
* <br>
function: parse_args <br>
description: parse the command line args. <br>
arguments: <br>
argc,argv you know what these are. <br>
return value: none. <br>
calls: none. <br>
globals: writes proxy_port, writes hostaddr. <br>
*************************************************************************** <br>
*/ <br>
*/ <br>
void parse_args (argc,argv) <br>
int argc; <br>
char **argv; <br>
{ <br>
int i; <br>
struct hostent *hostp; <br>
struct servent *servp; <br>
unsigned long inaddr; <br>
struct { <br>
char proxy_port [16]; <br>
char isolated_host [64]; <br>
char service_name [32]; <br>
} pargs; <br>
if (argc < 4) { <br>
printf("usage: %s <proxy-port> <host> <service-name|port-number> <br>
", <br>
argv[0]); <br>
exit(1); <br>
} <br>
strcpy(pargs.proxy_port,argv[1]); <br>
strcpy(pargs.isolated_host,argv[2]); <br>
strcpy(pargs.service_name,argv[3]); <br>
for (i = 0; i < strlen(pargs.proxy_port); i++) <br>
if (!isdigit(*(pargs.proxy_port + i))) <br>
break; <br>
if (i == strlen(pargs.proxy_port)) <br>
proxy_port = htons(atoi(pargs.proxy_port)); <br>
else { <br>
printf("%s: invalid proxy port <br>
",pargs.proxy_port); <br>
exit(0); <br>
} <br>
bzero(&hostaddr,sizeof(hostaddr)); <br>
hostaddr.sin_family = AF_INET; <br>
if ((inaddr = inet_addr(pargs.isolated_host)) != INADDR_NONE) <br>
bcopy(&inaddr,&hostaddr.sin_addr,sizeof(inaddr)); <br>
else if ((hostp = gethostbyname(pargs.isolated_host)) != NULL) <br>
bcopy(hostp->h_addr,&hostaddr.sin_addr,hostp->h_length); <br>
else { <br>
printf("%s: unknown host <br>
",pargs.isolated_host); <br>
exit(1); <br>
} <br>
if ((servp = getservbyname(pargs.service_name,TCP_PROTO)) != NULL) <br>
hostaddr.sin_port = servp->s_port; <br>
else if (atoi(pargs.service_name) > 0) <br>
hostaddr.sin_port = htons(atoi(pargs.service_name)); <br>
else { <br>
printf("%s: invalid/unknown service name or port number <br>
", <br>
pargs.service_name); <br>
exit(1); <br>
} <br>
} <br>
/*************************************************************************** <br>
* <br>
function: daemonize <br>
description: detach the server process from the current context, <br>
creating a pristine, predictable environment in which it <br>
will execute. <br>
arguments: <br>
servfd file descriptor in use by server. <br>
return value: none. <br>
calls: none. <br>
globals: none. <br>
*************************************************************************** <br>
*/ <br>
void daemonize (servfd) <br>
int servfd; <br>
{ <br>
int childpid, fd, fdtablesize; <br>
/* ignore terminal I/O, stop signals */ <br>
signal(SIGTTOU,SIG_IGN); <br>
signal(SIGTTIN,SIG_IGN); <br>
signal(SIGTSTP,SIG_IGN); <br>
/* fork to put us in the background (whether or not the user <br>
specified '&' on the command line */ <br>
if ((childpid = fork()) < 0) { <br>
fputs("failed to fork first child <br>
",stderr); <br>
exit(1); <br>
} <br>
else if (childpid > 0) <br>
exit(0); /* terminate parent, continue in child */ <br>
/* dissociate from process group */ <br>
if (setpgrp(0,getpid()) < 0) { <br>
fputs("failed to become process group leader <br>
",stderr); <br>
",stderr); <br>
exit(1); <br>
} <br>
/* lose controlling terminal */ <br>
if ((fd = open("/dev/tty",O_RDWR)) >= 0) { <br>
ioctl(fd,TIOCNOTTY,NULL); <br>
close(fd); <br>
} <br>
/* close any open file descriptors */ <br>
for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) <br>
if (fd != servfd) <br>
close(fd); <br>
/* set working directory to / to allow filesystems to be unmounted */ <br>
chdir("/"); <br>
/* clear the inherited umask */ <br>
umask(0); <br>
/* setup zombie prevention */ <br>
signal(SIGCLD,reap_status); <br>
} <br>
/*************************************************************************** <br>
* <br>
function: do_proxy <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -