关于正、反向代理的一些理解,附简单代理的实现代码

目录

一、正向代理

二、反向代理

三、正、反向代理的区别

四、一个简单代理的实现代码(C)


一、正向代理

正向代理,也就是传说中的代理,他的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我,其实正向代理很像地下交易的中间人。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。

从概念中可以得到如下信息:

  • 正向代理是位于客户端和源服务器之间的服务器
  • 客户端是知道源服务器的地址的,只是由于网络或其它原因访问不到
  • 源服务器并不知道实际要获取它内容的是访问不到它的客户端,在源服务器看来,它只是正常把服务器内容发给一个正常请求它内容的连接(这是基于正向代理不告诉源服务器的情况,一般情况也是这样),相当于隐藏了客户端
  • 正向代理代理的是客户端

下图是正向代理的结构简图

二、反向代理

是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。通俗点讲,就是说用户访问某个资源,其实访问的是反向代理,比如访问www.baidu.com,他可能访问的是www.baidu.com的反向代理服务器,反向代理服务器指向实际的资源服务器,然后从资源服务器取到,反馈给用户,一切神不知鬼不觉(用户感知不到)。

从上面概念得出以下信息:

  • 反向代理代理的是服务端
  • 客户端并不知道服务端的真实地址
  • 反向代理只是负责对外暴露接收请求,并将请求分给实际的服务器
  • 在客户端看来访问反向代理就是在访问实际的服务器

下面是反向代理的结构简图

三、正、反向代理的区别

虽然正向代理服务器和反向代理服务器所处的位置都是客户端和真实服务器之间,所做的事情也都是把客户端的请求转发给服务器,再把服务器的响应转发给客户端,但是二者之间还是有一定的差异的。

  • 正向代理其实是客户端的代理,帮助客户端访问其无法访问的服务器资源。反向代理则是服务器的代理,帮助服务器做负载均衡,安全防护等。
  • 正向代理一般是客户端架设的,比如在自己的机器上安装一个代理软件。而反向代理一般是服务器架设的,比如在自己的机器集群中部署一个反向代理服务器。
  • 正向代理中,服务器不知道真正的客户端到底是谁,以为访问自己的就是真实的客户端。而在反向代理中,客户端不知道真正的服务器是谁,以为自己访问的就是真实的服务器。
  • 正向代理和反向代理的作用和目的不同。正向代理主要是用来解决访问限制问题。而反向代理则是提供负载均衡、安全防护等作用。二者均能提高访问速度。

四、一个简单代理的实现代码(C)

#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<pthread.h>
#include <sys/file.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/param.h>

#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 <proxy-port> <host> <service-name|port-number null|--daemon> \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 <proxy-port> <host> <service-name|port-number null|--daemon> 
./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服务端并不知道访问它 的真实客户端是谁,只知道有请求过来,请求数据,而客户端却知道服务端是谁。

第一次写这么多,有问题欢迎指正!共同进步。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值