目录
一、正向代理
二、反向代理
三、正、反向代理的区别
四、一个简单代理的实现代码(C)
正向代理,也就是传说中的代理,他的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我,其实正向代理很像地下交易的中间人。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
从概念中可以得到如下信息:
下图是正向代理的结构简图
是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。通俗点讲,就是说用户访问某个资源,其实访问的是反向代理,比如访问www.baidu.com,他可能访问的是www.baidu.com的反向代理服务器,反向代理服务器指向实际的资源服务器,然后从资源服务器取到,反馈给用户,一切神不知鬼不觉(用户感知不到)。
从上面概念得出以下信息:
下面是反向代理的结构简图
虽然正向代理服务器和反向代理服务器所处的位置都是客户端和真实服务器之间,所做的事情也都是把客户端的请求转发给服务器,再把服务器的响应转发给客户端,但是二者之间还是有一定的差异的。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TCP_PROTO "tcp"
#define START_DAEMON "--daemon"
#define BUFFER_SIZE 1024
#define SUCCESS 0
#define ERROR -1
struct sockaddr_in hostaddr;
struct {
char proxy_port[16];
char isolated_host[64];
char service_name[32];
char daemon_args[16];
} pargs;
int proxy_port;
void init_daemon(int servfd)
{
int pid;
int fd;
//忽略终端I/O信号,STOP信号
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
if (pid=fork())
exit(0);
else if (pid< 0)
exit(1);
setsid();
if (pid=fork())
exit(0);
else if (pid< 0)
exit(1);
if ((fd = open("/dev/tty", O_RDWR)) >= 0)
{
ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
for (fd=0;fd< NOFILE;fd++)
if (fd != servfd) close(fd);
chdir("/");
umask(0);
//忽略SIGCHLD信号
signal(SIGCHLD,SIG_IGN);
return;
}
void parse_args(int argc, char ** argv)
{
int i;
struct hostent *hostp;
struct servent *servp;
unsigned long inaddr;
if (argc < 4) {
printf("usage: %s \n", argv[0]);
exit(1);
}
strcpy(pargs.proxy_port, argv[1]);
strcpy(pargs.isolated_host, argv[2]);
strcpy(pargs.service_name, argv[3]);
if (argc == 5)
strcpy(pargs.daemon_args, argv[4]);
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 \n", 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 \n", 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 \n", pargs.service_name);
exit(1);
}
}
void * handle_message(void * argv)
{
int s_c = * ((int *) argv);
fd_set rdfdset;
int iolen;
int clientSocket;
char buffer[BUFFER_SIZE] = {0};
if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
goto EXIT;
}
if(connect(clientSocket, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0)
{
perror("connect");
goto EXIT;
}
while (1)
{
FD_ZERO(&rdfdset);
FD_SET(s_c, &rdfdset);
FD_SET(clientSocket, &rdfdset);
if (select(FD_SETSIZE, &rdfdset, NULL, NULL, NULL) < 0)
{
perror("Select failed");
goto EXIT;
}
/*is the host sending data? */
if (FD_ISSET(clientSocket, &rdfdset)) {
if ((iolen = read(clientSocket, buffer, sizeof(buffer))) <= 0)
break;
write(s_c, buffer, iolen);
}
if (FD_ISSET(s_c, &rdfdset)) {
if ((iolen = read(s_c, buffer, sizeof(buffer))) <= 0)
break;
write(clientSocket, buffer, iolen);
}
}
EXIT:
printf("Exit \n");
close(s_c);
close(clientSocket);
}
void *proxy(int serverSocket)
{
int s_c;
int recvlen;
struct sockaddr_in clientAddr;
int addr_len = sizeof(clientAddr);
int err;
pthread_t serv_t;
while (1)
{
s_c = accept(serverSocket, (struct sockaddr *) &clientAddr, (socklen_t*)&addr_len);
if(s_c < 0)
{
perror("accept");
continue;
}
err = pthread_create(&serv_t, NULL, handle_message, &s_c);
if (err != 0)
{
printf("thread_create Failed:%s\n",strerror(errno));
}else{
printf("thread_create success\n");
pthread_detach(serv_t);
}
}
}
int main(int argc, char **argv)
{
int len=sizeof(int);
int flag = 1;
int ret;
int serverSocket;
struct sockaddr_in server_addr;
parse_args(argc, argv);
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return ERROR;
}
bzero(&server_addr, sizeof(server_addr));
//初始化服务器端的套接字,并用htons和htonl将端口和地址转成网络字节序
server_addr.sin_family = AF_INET;
server_addr.sin_port = proxy_port;
//ip可是是本服务器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if ( setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1)
{
perror("setsockopt");
exit(1);
}
//对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *)
//bind三个参数:服务器端的套接字的文件描述符,
if(bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect");
return ERROR;
}
//设置服务器上的socket为监听状态
if(listen(serverSocket, 5) < 0)
{
perror("listen");
return ERROR;
}
if (!strcmp(START_DAEMON, pargs.daemon_args))
init_daemon(serverSocket);
proxy(serverSocket);
close(serverSocket);
return SUCCESS;
}
该代码实现的是数据转发,其实既可以用作正向代理也可以用作反向代理,我使用的方式是正向代理,代理的是rsync端口59873,服务端ip为192.168.137.8,服务端rsync端口59872,编译生成simple_proxy可执行文件。
使用如下:
usage: ./simple_proxy
./simple_proxy 59873 ip 59872 --daemon
程序启动后会在后台以守护进程的方式存在,此时rsync客户端访问服务端是这样的
rsync -vzrtopg --progress --password-file=/etc/rsync_passwd --port=59873 testq@ip::test /var/log/rstest
访问的端口是代理端口59873,这样使用时rsync服务端并不知道访问它 的真实客户端是谁,只知道有请求过来,请求数据,而客户端却知道服务端是谁。
第一次写这么多,有问题欢迎指正!共同进步。