| 首页 | 学习 | 计算机 | 小说 | 动漫 | 论文 | 军事 | 科技 | 教育 | 哲学 | 历史 | 英语 | 笑话 | 
您现在的位置: 【书斋】 >> 计算机 >> 程序编程 >> 计算机正文 用户登录 新用户注册
一个简易的proxy程序的开发过程(1)           ★★
一个简易的proxy程序的开发过程(1)
一个简易的proxy程序的开发过程(1)
1、引言
很多人都看过Eric Steven Raymond写的<<The Cathedral and the Bazaar>> (大教堂与集市) 这篇文章吧。
这篇文章讲述了传统的开发小组开发方式和基于Internet的分散的开发方式(linux的开发方式,GNU软件的
开发方式)的区别,并且根据自己的一个程序的开发例子来讲述了The Bazaar开发方式的若干条重要原则。
不过,国内很多程序员,工作的时候还是采用的传统的开发方式,很难有机会在工作中体验这些原则。那么,
这个例子就给了大家又一个体验这些原则的过程。
这个例子,主要是运用了一些编程的技术,比如,socket编程,信号,进程,还有一些unix socket编程的较
高级论题。当然,这些都不是主要的,重要的是,体验一下集市的开发方式。

2、开发这个proxy程序的背景
我工作的时候,处在一个比较封闭的网络环境中。我的机器在局域网 (LAN) 之中,与外界的Internet相连采
用了代理的方式,有若干台unix服务器作为代理服务器,运行squid作为http的代理,运行socks作为socks 5
代理。应该说,这样的待遇,还算不错,:-), 要浏览网站,squid够用了;要运行ICQ, OICQ之类的程序,用
socks也够了。但是,我遇到了一个比较麻烦的问题,在这样的网络环境中,我没有办法用Outlook等工具收取
非来自公司邮件服务器的邮件(比方说,@linuxaid.com.cn, @163.net, @sina.com.cn 等等);也没有办法用
Gravity等工具来收取USENET上的讨论。当然,折衷的办法还是有,我可以用linux下的一些支持socks的邮件
客户端软件和新闻组阅读软件。但是,这样势必造成一些麻烦( 实际上我也这样做过 ),当我需要收取邮件
或者阅读新闻组的时候,我必须重新启动机器转换到linux操作系统中去,而当我要办公的时候,我又不得不
重新启动机器再转换到windows操作系统中来 ( 我不得不说,linux作为办公的桌面还是不如windows, 虽然这
句话肯定会惹恼很多linux fan :-) )。作为一个程序员,我当然不能忍受这种麻烦。我必须想办法来解决这
个问题。经过考虑,我有了一个好的想法。
这体现了The Bazaar原则一:
Every good work of software starts by scratching a developer's personal itch.
每一个软件的开发都是带有开发者自己的烙印。
3、初期设计
我需要的是一个程序,他能够做"二传手"的工作。他自身处在能同时连通外界目标服务器和我的机器的位置上。
我的机器把请求发送给他,他接受请求,把请求原封不动的抄下来发送给外界目标服务器;外界目标服务器
响应了请求,把回答发送给他,他再接受回答,把回答原封不动的抄下来发送给我的机器。这样,我的机器
实际上是把他当作了目标服务器( 由于是原封不动的转抄,请求和回答没有被修改 )。而他则是外界目标
服务器的客户端( 由于是原封不动的转抄,请求和回答没有被修改 )。我把这种代理服务程序叫做"二传手"。
原理图如下:

|--------------| |-----------------| |--------------------|
| |------------------>| |---------------->| |
| 我的机器 | | 代理服务程序 | | 目标服务器 |
| |<------------------| |<----------------| |
|--------------| |-----------------| |--------------------|

4、例子重用
The Bazaar原则二:
Good programmers know what to write. Great ones know what to rewrite (and reuse).
好的程序员知道写什么。而伟大的程序员知道重写和重用什么。
基于这个原则,我当然不会从头来写这个程序(其实,这个程序是一个很小的程序,没有必要一定要这么做。
但是,为了给大家,同时也是给我自己一个集市化的开发方式的体验,我还是这么做了,我先是写出来了一个
简单的程序---附在本文最后----然后才想起来去找找有没有类似的程序 :-), 结果浪费了很多时间)。
在网上找了找,花了大概半个小时( 和我写出第一个简单程序所花的时间差不多 :-) ),找到了这个程序。
程序如下:
------------------------------------------------------------------------------------------------
/****************************************************************************
program: proxyd
module: proxyd.c
summary: provides proxy tcp service for a host on an isolated network.

programmer: Carl Harris (ceharris@vt.edu)
date: 22 Feb 94

description:
This code implements a daemon process which listens for tcp connec-
tions on a specified port number. When a connection is established,
a child is forked to handle the new client. The child then estab-
lishes a tcp connection to a port on the isolated host. The child
then falls into a loop in which it writes data to the isolated host
for the client and vice-versa. Once a child has been forked, the
parent resumes listening for additional connections.

The name of the isolated host and the port to serve as proxy for,
as well as the port number the server listen on are specified as
command line arguments.
****************************************************************************/


#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>


#define TCP_PROTO "tcp"

int proxy_port; /* port to listen for proxy connections on */
struct sockaddr_in hostaddr; /* host addr assembled from gethostbyname() */

[1] [2] [3] [4] [5] [6] [7] [8] 下一页  


extern int errno; /* defined by libc.a */
extern char *sys_errlist[]; /* defined by libc.a */


void parse_args (int argc, char **argv);
void daemonize (int servfd);
void do_proxy (int usersockfd);
void reap_status (void);
void errorout (char *msg);



/****************************************************************************
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 socket
",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 port
",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! */

close(newsockfd);
}
}



/****************************************************************************
function: parse_args
description: parse the command line args.
arguments:
argc,argv you know what these are.

return value: none.
calls: none.
globals: writes proxy_port, writes hostaddr.
****************************************************************************/

void parse_args (argc,argv)
int argc;
char **argv;
{
int i;
struct hostent *hostp;
struct servent *servp;
unsigned long inaddr;
struct {
char proxy_port [16];
char isolated_host [64];
char service_name [32];
} pargs;


if (argc < 4) {
printf("usage: %s <proxy-port> <host> <service-name|port-number>
",
argv[0]);
exit(1);
}

strcpy(pargs.proxy_port,argv[1]);
strcpy(pargs.isolated_host,argv[2]);
strcpy(pargs.service_name,argv[3]);

for (i = 0; i < strlen(pargs.proxy_port); i++)
if (!isdigit(*(pargs.proxy_port + i)))
break;

if (i == strlen(pargs.proxy_port))

上一页  [1] [2] [3] [4] [5] [6] [7] [8] 下一页  

proxy_port = htons(atoi(pargs.proxy_port));
else {
printf("%s: invalid proxy port
",pargs.proxy_port);
exit(0);
}

bzero(&hostaddr,sizeof(hostaddr));
hostaddr.sin_family = AF_INET;
if ((inaddr = inet_addr(pargs.isolated_host)) != INADDR_NONE)
bcopy(&inaddr,&hostaddr.sin_addr,sizeof(inaddr));
else if ((hostp = gethostbyname(pargs.isolated_host)) != NULL)
bcopy(hostp->h_addr,&hostaddr.sin_addr,hostp->h_length);
else {
printf("%s: unknown host
",pargs.isolated_host);
exit(1);
}

if ((servp = getservbyname(pargs.service_name,TCP_PROTO)) != NULL)
hostaddr.sin_port = servp->s_port;
else if (atoi(pargs.service_name) > 0)
hostaddr.sin_port = htons(atoi(pargs.service_name));
else {
printf("%s: invalid/unknown service name or port number
",
pargs.service_name);
exit(1);
}
}



/****************************************************************************
function: daemonize
description: detach the server process from the current context,
creating a pristine, predictable environment in which it
will execute.
arguments:
servfd file descriptor in use by server.

return value: none.
calls: none.
globals: none.
****************************************************************************/

void daemonize (servfd)
int servfd;
{
int childpid, fd, fdtablesize;

/* ignore terminal I/O, stop signals */
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);

/* fork to put us in the background (whether or not the user
specified '&' on the command line */

if ((childpid = fork()) < 0) {
fputs("failed to fork first child
",stderr);
exit(1);
}
else if (childpid > 0)
exit(0); /* terminate parent, continue in child */

/* dissociate from process group */
if (setpgrp(0,getpid()) < 0) {
fputs("failed to become process group leader
",stderr);
exit(1);
}

/* lose controlling terminal */
if ((fd = open("/dev/tty",O_RDWR)) >= 0) {
ioctl(fd,TIOCNOTTY,NULL);
close(fd);
}

/* close any open file descriptors */
for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++)
if (fd != servfd)
close(fd);

/* set working directory to / to allow filesystems to be unmounted */
chdir("/");

/* clear the inherited umask */
umask(0);

/* setup zombie prevention */
signal(SIGCLD,reap_status);

}



/****************************************************************************
function: do_proxy
description: does the actual work of virtually connecting a client to
the telnet service on the isolated host.
arguments:
usersockfd socket to which the client is connected.

return value: none.
calls: none.
globals: reads hostaddr.
****************************************************************************/

void do_proxy (usersockfd)
int usersockfd;
{
int isosockfd;
fd_set rdfdset;
int connstat;
int iolen;
char buf [2048];

/* open a socket to connect to the isolated host */
if ((isosockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
errorout("failed to create socket to host");

/* attempt a connection */
connstat = connect(isosockfd,
(struct sockaddr *) &hostaddr,
sizeof(hostaddr));
switch (connstat) {
case 0:
break;
case ETIMEDOUT:
case ECONNREFUSED:
case ENETUNREACH:
strcpy(buf,sys_errlist[errno]);
strcat(buf,"
");
write(usersockfd,buf,strlen(buf));

[1] [2] [3] [4] 下一页  

在百度搜索:一个简易的proxy程序的开发过程(1)
  • 上一个计算机:

  • 下一个计算机:
  • 相 关 文 章
  • Oracle应用Linux开发C

  • 一个简易的UDP Proxy程序

  • 一个简易的proxy程序的开

  • 用C语言编写简单的接口程

  • 一个简单的口令保护程式

  • 在C程序中显示汉字

  • 在C程序中处理汉字

  • C语言编的MD5主程序

  • 用C语言建立多个PRI DOS

  • 自解密的加密程序的制作

  • 写远程缓冲区溢出漏洞利

  • 简单的时间/日期客户程序

  • 怎样用C语言编写一个DOS

  • 自己写程序揭开cmos密码

  • 如何将多个C语言模块组合

  • 用C语言小程序来解决大问

  • Using make to Simplify

  • 为 Linux 应用程序编写 

  • Linux 内核模块和驱动程

  • Linux 应用程序如何处理

  • 编写 Linux 操作系统下的

  • C语言创建自己的设备(一

  • Linux 下面使用 mtrace 

  • 系统调用跟我学(1)

  • 用C语言实现Ping程序功能

  • Linux的nfsd存在漏洞允许

  • Device Drivers(设备驱

  • The Linux Kernel Sourc

  • 第一篇:优美的程序需要

  • /proc文件系统