📄 276.htm
字号:
description: does the actual work of virtually connecting a client to <br>
the telnet service on the isolated host. <br>
arguments: <br>
usersockfd socket to which the client is connected. <br>
return value: none. <br>
calls: none. <br>
globals: reads hostaddr. <br>
*************************************************************************** <br>
*/ <br>
void do_proxy (usersockfd) <br>
int usersockfd; <br>
{ <br>
int isosockfd; <br>
fd_set rdfdset; <br>
int connstat; <br>
int iolen; <br>
char buf [2048]; <br>
/* open a socket to connect to the isolated host */ <br>
if ((isosockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) <br>
errorout("failed to create socket to host"); <br>
/* attempt a connection */ <br>
connstat = connect(isosockfd, <br>
(struct sockaddr *) &hostaddr, <br>
sizeof(hostaddr)); <br>
switch (connstat) { <br>
case 0: <br>
break; <br>
case ETIMEDOUT: <br>
case ECONNREFUSED: <br>
case ENETUNREACH: <br>
strcpy(buf,sys_errlist[errno]); <br>
strcat(buf," <br>
"); <br>
write(usersockfd,buf,strlen(buf)); <br>
close(usersockfd); <br>
exit(1); /* die peacefully if we can't establish a connection */ <br>
break; <br>
default: <br>
errorout("failed to connect to host"); <br>
} <br>
/* now we're connected, serve fall into the data echo loop */ <br>
while (1) { <br>
/* Select for readability on either of our two sockets */ <br>
FD_ZERO(&rdfdset); <br>
FD_SET(usersockfd,&rdfdset); <br>
FD_SET(isosockfd,&rdfdset); <br>
if (select(FD_SETSIZE,&rdfdset,NULL,NULL,NULL) < 0) <br>
errorout("select failed"); <br>
/* is the client sending data? */ <br>
if (FD_ISSET(usersockfd,&rdfdset)) { <br>
if ((iolen = read(usersockfd,buf,sizeof(buf))) <= 0) <br>
break; /* zero length means the client disconnected */ <br>
write(isosockfd,buf,iolen); /* copy to host -- blocking semantics */ <br>
} <br>
/* is the host sending data? */ <br>
if (FD_ISSET(isosockfd,&rdfdset)) { <br>
if ((iolen = read(isosockfd,buf,sizeof(buf))) <= 0) <br>
break; /* zero length means the host disconnected */ <br>
write(usersockfd,buf,iolen); /* copy to client -- blocking semantics * <br>
/ <br>
} <br>
} <br>
/* we're done with the sockets */ <br>
close(isosockfd); <br>
close(usersockfd); <br>
} <br>
/*************************************************************************** <br>
* <br>
function: errorout <br>
description: displays an error message on the console and kills the <br>
current process. <br>
arguments: <br>
msg message to be displayed. <br>
return value: none -- does not return. <br>
calls: none. <br>
globals: none. <br>
*************************************************************************** <br>
*/ <br>
void errorout (msg) <br>
char *msg; <br>
{ <br>
FILE *console; <br>
console = fopen("/dev/console","a"); <br>
fprintf(console,"proxyd: %s <br>
",msg); <br>
fclose(console); <br>
exit(1); <br>
} <br>
/*************************************************************************** <br>
* <br>
function: reap_status <br>
description: handle a SIGCLD signal by reaping the exit status of the <br>
perished child, and discarding it. <br>
arguments: none. <br>
return value: none. <br>
calls: none. <br>
globals: none. <br>
*************************************************************************** <br>
*/ <br>
void reap_status () <br>
{ <br>
int pid; <br>
union wait status; <br>
while ((pid = wait3(&status,WNOHANG,NULL)) > 0) <br>
; /* loop while there are more dead children */ <br>
} <br>
---------------------------------------------------------------------------- <br>
-------------------- <br>
( 这个程序来自水木清华BBS精华版 ) <br>
看了这个程序,我细化了我的初步设计: 程序监听服务端口,接受客户端连接,派生出 <br>
子进程处理连接, <br>
同时连接远程机器的服务端口,然后开始完成"二传手"的工作。 <br>
当然,这个小程序也有不足的地方: <br>
a、他只能监听一个服务端口,只能连接一个远程机器的服务端口。 <br>
b、他采用了子进程的方式,如果客户端连接很多,就会给服务器造成比较大的压力。 <br>
c、他只能监听tcp,而不能作为udp的代理服务器 ( 广大 OICQ 用户都知道,这个程序 <br>
不能用来做 OICQ <br>
代理)。 <br>
d、他只能用命令行的方式读入服务端口,远程服务器地址和端口,不能用配置文件的方 <br>
式。 <br>
所以,我还是决定继续完善我自己的程序,而不是用他。 <br>
The Bazaar原则三: <br>
Plan to throw one away; you will, anyhow. <br>
5、第一版的代码 <br>
我的小程序,第一版本如下: <br>
---------------------------------------------------------------------------- <br>
-------------------- <br>
/*************************************************************** <br>
Program: sp.c <br>
Description: a smart proxy <br>
Author: Alan Chen (ariesram@may10.ca) <br>
Date: July 10, 2001 <br>
***************************************************************/ <br>
#include <stdio.h> <br>
#include <string.h> <br>
#include <sys/types.h> <br>
#include <sys/socket.h> <br>
#include <sys/time.h> <br>
#include <sys/wait.h> <br>
#include <unistd.h> <br>
#include <netinet/in.h> <br>
int do_proxy(int infd); <br>
int max(int i, int j); <br>
void waitchild(int); <br>
int main(void) { <br>
struct sockaddr_in servaddr, clientaddr; <br>
int listenfd, connfd; <br>
int clientlen; <br>
pid_t chpid; <br>
bzero(&servaddr, sizeof(servaddr)); <br>
servaddr.sin_family = AF_INET; <br>
servaddr.sin_port = htons(7000); <br>
servaddr.sin_addr.s_addr = INADDR_ANY; <br>
listenfd = socket(AF_INET, SOCK_STREAM, 0); <br>
if(listenfd < 0) { <br>
printf("socket error <br>
"); <br>
exit(-1); <br>
} <br>
if( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { <br>
printf("bind error <br>
"); <br>
exit(-1); <br>
} <br>
if( listen(listenfd, 5) < 0 ) { <br>
printf("listen error <br>
"); <br>
exit(-1); <br>
} <br>
signal(SIGCHLD, waitchild); <br>
for(;;) { <br>
connfd = accept( listenfd, (struct sockaddr *)&clientaddr, <br>
&clientlen ); <br>
if( connfd < 0 ) { <br>
printf("accept error <br>
"); <br>
exit(-1); <br>
exit(-1); <br>
} <br>
if( (chpid = fork()) == -1 ) { <br>
printf("fork error <br>
"); <br>
exit(-1); <br>
} <br>
if( chpid == 0 ) { <br>
close(listenfd); <br>
do_proxy(connfd); <br>
exit(0); <br>
} <br>
if( chpid > 0 ) { <br>
close(connfd); <br>
} <br>
} <br>
exit(0); <br>
} <br>
int do_proxy(int infd) { <br>
struct sockaddr_in rout; <br>
int outfd; <br>
int maxfd; <br>
int count = 65535; <br>
int n; <br>
fd_set set; <br>
char buf[count]; <br>
bzero(&rout, sizeof(rout)); <br>
rout.sin_family = AF_INET; <br>
rout.sin_port = htons(7001); <br>
rout.sin_addr.s_addr = inet_addr("127.0.0.1"); <br>
if( (outfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { <br>
printf("socket error <br>
"); <br>
exit(-1); <br>
} <br>
if( connect(outfd, (struct sockaddr *)&rout, sizeof(rout)) < 0 ) { <br>
printf("connect error <br>
"); <br>
exit(-1); <br>
} <br>
while(1) { <br>
FD_ZERO(&set); <br>
FD_SET(infd, &set); <br>
FD_SET(outfd, &set); <br>
maxfd = max(outfd, infd); <br>
if( select(maxfd + 1, &set, NULL, NULL, NULL) < 0 ) { <br>
perror("select error:"); <br>
exit(-1); <br>
} <br>
if( FD_ISSET(infd, &set) ) { <br>
n = read(infd, (void *)buf, count); <br>
if( n <= 0) <br>
break; <br>
if( write(outfd, (const void *)buf, n) != n ) { <br>
printf("write error <br>
"); <br>
continue; <br>
} <br>
} <br>
if( FD_ISSET(outfd, &set) ) { <br>
n = read(outfd, (void *)buf, count); <br>
if( n <= 0) <br>
break; <br>
if( write(infd, (const void *)buf, n) != n ) { <br>
printf("write error <br>
"); <br>
continue; <br>
continue; <br>
} <br>
} <br>
} <br>
close(infd); <br>
close(outfd); <br>
} <br>
int max(int i, int j) { <br>
return i>j?i:j; <br>
} <br>
void waitchild(int signo) { <br>
int status; <br>
pid_t childpid; <br>
if( (childpid = waitpid(-1, &status, WNOHANG)) < 0 ) { <br>
printf("wait error <br>
"); <br>
exit(1); <br>
} <br>
printf("child %d quitted <br>
", childpid); <br>
return; <br>
} <br>
---------------------------------------------------------------------------- <br>
-------------------- <br>
下面简单解释一下程序。对 socket 网络编程比较熟悉的就不要看了。:-) <br>
bzero(&servaddr, sizeof(servaddr)); <br>
servaddr.sin_family = AF_INET; <br>
servaddr.sin_port = htons(7000); <br>
servaddr.sin_addr.s_addr = INADDR_ANY; <br>
给出一个sockaddr_in结构,定义了服务器的端口号和地址。 <br>
listenfd = socket(AF_INET, SOCK_STREAM, 0); <br>
socket()函数返回一个socket类型的描述字,类型为AF_INET ( IPv4 ), SOCK_STREAM <br>
( TCP ) . <br>
if(listenfd < 0) { <br>
printf("socket error <br>
"); <br>
exit(-1); <br>
} <br>
如果socket()函数返回值为小于0, 则表示出错。 <br>
if( bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 ) { <br>
printf("bind error <br>
"); <br>
exit(-1); <br>
} <br>
绑定描述字和服务器地址端口。如果bind()函数返回值为小于0, 则表示出错。 <br>
signal(SIGCHLD, waitchild); <br>
指定SIGCHLD信号的处理函数为waitchild()。当主进程fork()出的子进程结束的时候, <br>
主进程会收到 <br>
一个SIGCHLD信号,内核发送这个信号的目的是为了让主进程有机会能够检查子进程的退 <br>
出状态,并 <br>
做一些清理工作( 如果必要的话 )。如果主进程不处理SIGCHLD信号,子进程将会变成 <br>
僵尸进程, <br>
直到主进程退出,被init进程接管,被init进程清理掉。 <br>
waitchild() 函数如下: <br>
void waitchild(int signo) { <br>
int status; <br>
pid_t childpid; <br>
if( (childpid = waitpid(-1, &status, WNOHANG)) < 0 ) { <br>
printf("wait error <br>
"); <br>
exit(1); <br>
} <br>
printf("child %d quitted <br>
", childpid); <br>
return; <br>
} <br>
注意:signal处理函数必须定义成 void func(int)形式。 <br>
waitpid(-1, &status, WNOHANG)等待子进程退出,并且获取子进程的退出状态保存到s <br>
tatus里。 <br>
printf("child %d quitted <br>
", childpid);打印子进程的进程号。 <br>
if( listen(listenfd, 5) < 0 ) { <br>
printf("listen error <br>
"); <br>
exit(-1); <br>
} <br>
启动监听,指定等待队列长度为5。如果listen()函数返回值为小于0, 则表示出错。 <br>
for(;;) { <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -