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))
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)); close(usersockfd); exit(1); /* die peacefully if we can't establish a connection */ break; default: errorout("failed to connect to host"); } /* now we're connected, serve fall into the data echo loop */ while (1) { /* Select for readability on either of our two sockets */ FD_ZERO(&rdfdset); FD_SET(usersockfd,&rdfdset); FD_SET(isosockfd,&rdfdset); if (select(FD_SETSIZE,&rdfdset,NULL,NULL,NULL) < 0) errorout("select failed"); /* is the client sending data? */ if (FD_ISSET(usersockfd,&rdfdset)) { if ((iolen = read(usersockfd,buf,sizeof(buf))) <= 0) break; /* zero length means the client disconnected */ write(isosockfd,buf,iolen); /* copy to host -- blocking semantics */ } /* is the host sending data? */ if (FD_ISSET(isosockfd,&rdfdset)) { if ((iolen = read(isosockfd,buf,sizeof(buf))) <= 0) break; /* zero length means the host disconnected */ write(usersockfd,buf,iolen); /* copy to client -- blocking semantics */ } } /* we're done with the sockets */ close(isosockfd); close(usersockfd); } /**************************************************************************** function: errorout description: displays an error message on the console and kills the current process. arguments: msg message to be displayed. return value: none -- does not return. calls: none. globals: none. ****************************************************************************/ void errorout (msg) char *msg; { FILE *console; console = fopen("/dev/console","a"); fprintf(console,"proxyd: %s ",msg); fclose(console); exit(1); } /**************************************************************************** function: reap_status description: handle a SIGCLD signal by reaping the exit status of the perished child, and discarding it. arguments: none. return value: none. calls: none. globals: none. ****************************************************************************/ void reap_status () { int pid; union wait status; while ((pid = wait3(&status,WNOHANG,NULL)) > 0) ; /* loop while there are more dead children */ } ------------------------------------------------------------------------------------------------ ( 这个程序来自水木清华BBS精华版 ) 看了这个程序,我细化了我的初步设计: 程序监听服务端口,接受客户端连接,派生出子进程处理连接, 同时连接远程机器的服务端口,然后开始完成"二传手"的工作。 当然,这个小程序也有不足的地方: a、他只能监听一个服务端口,只能连接一个远程机器的服务端口。 b、他采用了子进程的方式,如果客户端连接很多,就会给服务器造成比较大的压力。 c、他只能监听tcp,而不能作为udp的代理服务器 ( 广大 OICQ 用户都知道,这个程序不能用来做 OICQ 代理)。 d、他只能用命令行的方式读入服务端口,远程服务器地址和端口,不能用配置文件的方式。 所以,我还是决定继续完善我自己的程序,而不是用他。 The Bazaar原则三: Plan to throw one away; you will, anyhow. 5、第一版的代码 我的小程序,第一版本如下: ------------------------------------------------------------------------------------------------ /*************************************************************** Program: sp.c Description: a smart proxy Author: Alan Chen (ariesram@may10.ca) Date: July 10, 2001 ***************************************************************/ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/wait.h> #include <unistd.h> #include <netinet/in.h> int do_proxy(int infd); int max(int i, int j); void waitchild(int); int main(void) { struct sockaddr_in servaddr, clientaddr; int listenfd, connfd;
int clientlen; pid_t chpid; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(7000); servaddr.sin_addr.s_addr = INADDR_ANY; listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd < 0) { printf("socket error "); exit(-1); } if( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { printf("bind error "); exit(-1); } if( listen(listenfd, 5) < 0 ) { printf("listen error "); exit(-1); } signal(SIGCHLD, waitchild); for(;;) { connfd = accept( listenfd, (struct sockaddr *)&clientaddr, &clientlen ); if( connfd < 0 ) { printf("accept error "); exit(-1); } if( (chpid = fork()) == -1 ) { printf("fork error "); exit(-1); } if( chpid == 0 ) { close(listenfd); do_proxy(connfd); exit(0); } if( chpid > 0 ) { close(connfd); } } exit(0); } int do_proxy(int infd) { struct sockaddr_in rout; int outfd; int maxfd; int count = 65535; int n; fd_set set; char buf[count]; bzero(&rout, sizeof(rout)); rout.sin_family = AF_INET; rout.sin_port = htons(7001); rout.sin_addr.s_addr = inet_addr("127.0.0.1"); if( (outfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error "); exit(-1); } if( connect(outfd, (struct sockaddr *)&rout, sizeof(rout)) < 0 ) { printf("connect error "); exit(-1); } while(1) { FD_ZERO(&set); FD_SET(infd, &set); FD_SET(outfd, &set); maxfd = max(outfd, infd); if( select(maxfd + 1, &set, NULL, NULL, NULL) < 0 ) { perror("select error:"); exit(-1); } if( FD_ISSET(infd, &set) ) { n = read(infd, (void *)buf, count); if( n <= 0) break; if( write(outfd, (const void *)buf, n) != n ) { printf("write error "); continue; } } if( FD_ISSET(outfd, &set) ) { n = read(outfd, (void *)buf, count); if( n <= 0) break; if( write(infd, (const void *)buf, n) != n ) { printf("write error "); continue; } } } close(infd); close(outfd); } int max(int i, int j) { return i>j?i:j; } void waitchild(int signo) { int status; pid_t childpid; if( (childpid = waitpid(-1, &status, WNOHANG)) < 0 ) { printf("wait error "); exit(1); } printf("child %d quitted ", childpid); return; } ------------------------------------------------------------------------------------------------ 下面简单解释一下程序。对 socket 网络编程比较熟悉的就不要看了。:-) bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(7000); servaddr.sin_addr.s_addr = INADDR_ANY; 给出一个sockaddr_in结构,定义了服务器的端口号和地址。 listenfd = socket(AF_INET, SOCK_STREAM, 0); socket()函数返回一个socket类型的描述字,类型为AF_INET ( IPv4 ), SOCK_STREAM ( TCP ) . if(listenfd < 0) { printf("socket error "); exit(-1); } 如果socket()函数返回值为小于0, 则表示出错。 if( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { printf("bind error "); exit(-1); } 绑定描述字和服务器地址端口。如果bind()函数返回值为小于0, 则表示出错。 signal(SIGCHLD, waitchild); 指定SIGCHLD信号的处理函数为waitchild()。当主进程fork()出的子进程结束的时候,主进程会收到 一个SIGCHLD信号,内核发送这个信号的目的是为了让主进程有机会能够检查子进程的退出状态,并 做一些清理工作( 如果必要的话 )。如果主进程不处理SIGCHLD信号,子进程将会变成僵尸进程, 直到主进程退出,被init进程接管,被init进程清理掉。 waitchild() 函数如下: void waitchild(int signo) { int status; pid_t childpid; if( (childpid = waitpid(-1, &status, WNOHANG)) < 0 ) { printf("wait error "); exit(1); } printf("child %d quitted ", childpid); return; } 注意:signal处理函数必须定义成 void func(int)形式。 waitpid(-1, &status, WNOHANG)等待子进程退出,并且获取子进程的退出状态保存到status里。 printf("child %d quitted ", childpid);打印子进程的进程号。 if( listen(listenfd, 5) < 0 ) { printf("listen error "); exit(-1); } 启动监听,指定等待队列长度为5。如果listen()函数返回值为小于0, 则表示出错。 for(;;) { connfd = accept( listenfd, (struct sockaddr *)&clientaddr, &clientlen ); if( connfd < 0 ) { printf("accept error "); exit(-1); } if( (chpid = fork()) == -1 ) { printf("fork error "); exit(-1); } if( chpid == 0 ) { close(listenfd); do_proxy(connfd); exit(0); } if( chpid > 0 ) { close(connfd); } } 在for(;;){}这个无限循环中,进程阻塞于accept。 accept( listenfd, (struct sockaddr *)&clientaddr, &clientlen ) 等待客户端连接,如果连接成功,则在clientaddr中返回客户端的IP地址以及端口号,协议类型等信息,同时 clientaddr的长度存于clientlen中。accept返回socket连接描述字connfd.如果accept()函数返回值为小于0, 则表示出错。 连接成功,主进程采用fork()派生子进程。如果FORK()函数返回值为小于0, 则表示出错。 在主进程中( chpid > 0 ),关闭connfd描述字,并继续for(;;){}循环。在子进程中( chpid == 0 ),关闭 listenfd监听socket描述字,并调用do_proxy()函数 ( 稍候介绍,用于完成proxy的工作 )。等待do_proxy ()函数返回,并且退出子进程。 注意:fork() 函数是调用一次,返回两次,一次返回在主进程中,一次返回在子进程中。 下面介绍do_proxy()函数。 bzero(&rout, sizeof(rout)); rout.sin_family = AF_INET; rout.sin_port = htons(7001); rout.sin_addr.s_addr = inet_addr("127.0.0.1"); 定义连接的远程服务端口。由于这个程序是基于测试目的,为了方便,我把远程服务定义为本机的7001端口 ( 也就是说,实际上走的是loopback interface )。 if( (outfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket error "); exit(-1); } socket()函数返回一个socket类型的描述字,类型为AF_INET ( IPv4 ), SOCK_STREAM ( TCP ) . 如果socket()函数返回值为小于0, 则表示出错。 if( connect(outfd, (struct sockaddr *)&rout, sizeof(rout)) < 0 ) { printf("connect error "); exit(-1); } connect()函数连接远程服务地址和端口,如果connect()函数返回值为小于0, 则表示出错。 在while(1) { } 无限循环中: FD_ZERO(&set); 清空fd_set FD_SET(infd, &set); FD_SET(outfd, &set); 把infd ( 是从主程序中传进来的,就是连接描述字connfd ), outfd ( 连接远程服务的描述字 )放进 fd_set。 maxfd = max(outfd, infd); 取两个描述字的最大值。 max() 函数定义如下: int max(int i, int j) { return i>j?i:j; } 很简单,就不用解释了。 if( select(maxfd + 1, &set, NULL, NULL, NULL) < 0 ) { perror("select error:"); exit(-1); } 阻塞于 select() 函数, 等待infd, outfd中任意描述字可读。 这里稍微解释一下:maxfd + 1, select函数要求第一个参数是集合中描述字最大值加1 ( 很多人常常忘记了 加上1,结果导致select函数出错 ) 。我把可写,异常两个集合都定义为空,因为我们不必关心这两个集合。 超时设置为NULL, 这表示如果没有描述字不可读,将永远阻塞在select 函数中。( 在以后的版本里面,我将修改 这一函数调用,以增强程序性能。如果select()函数返回值为小于0, 则表示出错。 if( FD_ISSET(infd, &set) ) { n = read(infd, (void *)buf, count); if( n <= 0) break; if( write(outfd, (const void *)buf, n) != n ) { printf("write error "); continue; } } 如果select返回值大于0,检测是否infd可读。如果可读,则从infd中读出数据,并写回到outfd中。这里, 如果read返回值小于或者等于0,表示服务器写入了终止符号或者服务器停止服务 ( 这里的情况比较复杂,需要注意。 )如果read出错,则终止循环。如果write写入outfd的字节数不为n则表示write出错 ( 原因可能是客户端终止或者其他异常情况 )。 但是,需要注意的是,当write出错的时候,我们并不退出,而是继续 while(1) { }循环。 if( FD_ISSET(outfd, &set) ) { n = read(outfd, (void *)buf, count); if( n <= 0) break; if( write(infd, (const void *)buf, n) != n ) { printf("write error "); continue; } } 如果select返回值大于0,检测是否outfd可读。如果可读,则从outfd中读出数据,并写回到infd中。这里, 如果read返回值小于或者等于0,表示服务器写入了终止符号或者服务器停止服务 ( 这里的情况比较复杂,需要注意。
上一页 [1] [2] [3] [4] [5] [6] [7] [8] 下一页
)如果read出错,则终止循环。如果write写入outfd的字节数不为n则表示write出错 ( 原因可能是客户端终止或者其他异常情况 )。 但是,需要注意的是,当write出错的时候,我们并不退出,而是继续 while(1) { }循环。 这一部分就是初步设计中的思想的实现。就是这两段程序完成了"二传手"的工作。 close(infd); close(outfd); 当循环因为服务端或者客户端终止或者其他出错退出,则关闭两个描述字,并返回。 5、测试第一版的程序 为了测试我的小程序是否能够按希望的方式运行并且得到正确的结果,我写了另外一个小程序用来辅助测试 的工作。 程序清单如下: ----------------------------------------------------------------------------------------------- /******************************************************* Program: echos.c Description: an echo server Author: Alan Chen (ariesram@may10.ca) Date: July, 10, 2001 *******************************************************/ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> void do_echo(int); void waitchild(int signo); int main(void) { struct sockaddr_in servaddr, clientaddr; int clientlen; int listenfd, connfd; pid_t childpid; listenfd = socket(AF_INET, SOCK_STREAM, 0); if( listenfd < 0 ) { printf("socket error "); exit(1); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(7001); servaddr.sin_addr.s_addr = INADDR_ANY; if( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { printf("bind error "); exit(1); } if( listen(listenfd, 5) < 0 ) { printf("listen error "); exit(1); } signal(SIGCHLD, waitchild); while(1) { if( (connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen)) < 0 ) { printf("accept error "); exit(1); } if( (childpid = fork() ) < 0 ) { printf("fork error "); exit(1); } if( childpid == 0 ) { close(listenfd); do_echo(connfd); exit(0); } close(connfd); } exit(0); } void do_echo(int fd) { int n; char buf[1024]; while(1) { if( (n = read(fd, (void *)&buf, 1024)) <= 0 ) break; if( (n = write(fd, (void *)&buf, n)) <= 0) break; } } void waitchild(int signo) { int status; pid_t chpid; if( (chpid = waitpid(-1, &status, WNOHANG)) < 0 ) { printf("waitpid error "); exit(-1); } printf("child %d quitted. ", chpid); return; } ----------------------------------------------------------------------------------------------- 这个程序比较简单,功能是把客户端输入的字符返回给客户端。当客户端终止时,则停止子进程。 程序解释完了,我们来看一下运行结果。 首先,编译这两个程序。 gcc -o sp sp.c gcc -o echos echos.c 运行. ./sp ./echos 看看程序初始化的时候端口的状态。 [alan@ariesram proxy]$ netstat -na | grep 700 tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN sp, echos分别监听两个端口,7000 和 7001。 启动一个客户端,连接sp的服务端口7000。 [alan@ariesram proxy]$ telnet localhost 7000 Trying 127.0.0.1... Connected to ariesram. Escape character is '^]'. 再来看看端口的状态。 [alan@ariesram alan]$ netstat -na | grep 700 tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:32769 127.0.0.1:7000 ESTABLISHED tcp 0 0 127.0.0.1:7001 127.0.0.1:32770 ESTABLISHED tcp 0 0 127.0.0.1:32770 127.0.0.1:7001 ESTABLISHED tcp 0 0 127.0.0.1:7000 127.0.0.1:32769 ESTABLISHED 其中, tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN 仍然处在监听状态。 而 tcp 0 0 127.0.0.1:32769 127.0.0.1:7000 ESTABLISHED 是我启动的telnet连接到sp服务端口的连接。 同时,sp发起了一个到目的服务端口7001的连接。 tcp 0 0 127.0.0.1:32770 127.0.0.1:7001 ESTABLISHED 另外, tcp 0 0 127.0.0.1:7000 127.0.0.1:32769 ESTABLISHED tcp 0 0 127.0.0.1:7001 127.0.0.1:32770 ESTABLISHED 分别是sp代理服务程序连接客户端和远程目标服务端口连接代理服务程序的连接。如果是remote方式 的话,是看不到这两个连接的。 在telnet客户端输入字符串做测试,看是否能够把输入字符串原样返回。 [alan@ariesram proxy]$ telnet localhost 7000 Trying 127.0.0.1... Connected to ariesram. Escape character is '^]'. asdf asdf sadf sadf asdfasdfasdfasfd asdfasdfasdfasfd 结果显示,我们的程序是成功的。:-) 退出telnet客户端,再来看看端口的状态。 [alan@ariesram proxy]$ netstat -na | grep 700 tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:32769 127.0.0.1:7000 TIME_WAIT tcp 0 0 127.0.0.1:32770 127.0.0.1:7001 TIME_WAIT 我们可以看到,由 telnet 客户端发起的连接和代理服务程序sp发起的连接都处于close过程的TIME_WAIT 状态。该状态的持续时间是最长分节生命周期 MSL ( maximum segment lifetime ) 的两倍,有时候称作 2MSL。 存在TIME_WAIT状态的两个理由: 1、实现终止TCP全双工连接的可靠性。 2、允许老的重复分节在网络中消逝。 而其中, tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN 仍然处在监听状态, 直到echos, sp两个程序退出。 6、小结 以上讲述了第一版的开发以及测试过程。我们看到,我的初步设想是能够实现的。接下来需要做的是 将代理服务程序修改成为一个可用的版本。需要做的事情是: a、修改程序运行方式,使其能从命令行读入 option,设定监听端口和所要连接的远程服务地址以及 端口。 b、使程序能够以后台方式运行 ,而不是前台方式,成为一个真正的服务程序。( 现在的版本当用户 退出控制台的时候会终止运行。) 进一步的工作是: c、使程序能够监听多个端口,并且连接多个远程服务。 d、使程序能够从配置文件中读取设定监听端口和所要连接的远程服务地址以及端口以满足多种服务并存 的需要。 这些工作我将在下一部分文章中描述。