代理服务器实现流程
- 创建一个监听服务器,监听客户端的连接。
- 客户端连接上后,将客户端要上网的信息,发给网络web服务器,
- 然后从那里读取数据,再发给客户端。
注:客户端是通过浏览器的设置,来设置我的地址和端口,一旦访问某个域名,就会和代理服务器连接。
代理服务器本身既是服务器,又是客户端。对浏览器来说,是服务器,对网页服务器来说是客户端。例如:代理服务器放在国外的主机上,可实现翻墙,当访问敏感数据,经过防火墙易被查出来了。
浏览器设置:
代理服务器代码:
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//创建监听套接字
int create_listensocket(void)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
int n = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); //地址复用
struct sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(2020);
sin.sin_addr.s_addr = INADDR_ANY; //listen any add
//bangding
if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("bind");
}
listen(fd, 50);
return fd;
}
//替换字符串
void replace(char *in, char *out)
{
//取出文件名
char filename[30]={0};
sscanf(in, "%*[^//]//%*[^/]%s", filename); //略过前面
printf("文件名是%s\n",filename);
//取出后面部分+前面的部分GET (HTTP/1.1...)
char *tail = strstr(in, "HTTP/"); //搜索HTTP子字符串
char method[10] = {0};
sscanf(in, "%s", method); //s碰到空格就会停下来
//再组合起来
sprintf(out, "%s %s %s", method, filename, tail);
//GET http://www.163.com/abc.txt HTTP/1.1
//--->GET /abc.txt HTTP/1.1
}
//处理客户端的读写
void handleclient(int fd)
{
//1 从客户端读取要上网的信息
char buffer[1024*1024]={0};
int nread = read(fd, buffer, sizeof(buffer));
//打出来
printf("%s\n",buffer);
//GET http://www.163.com/abc.txt HTTP/1.1
//Host: www.163.com
//User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
//Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
//Accept-Language: en-US,en;q=0.5
//Accept-Encoding: gzip, deflate
//Connection: keep-alive
//Upgrade-Insecure-Requests: 1
//2 转发给真正服务器
//需要将第一行域名变成文件名,如果单有域名没有文件名,则使用"/"
char buffernew[1024*1024]={0};
replace(buffer, buffernew);
printf("经过替换后的字符串是%s\n", buffernew);
//输出了一团为二进制(真正的请求里会有二进制,不能简单看做字符串,否则网页显示不完整)
//3 连接真正的服务器,把替换后的东西发给它
//解析域名
char hostname[30]={0};
sscanf(buffer, "%*[^//]//%[^/]",hostname);
printf("域名是%s\n",hostname);
//域名转ip
struct hostent *ent = gethostbyname(hostname);
//构建服务器的地址结构体
struct sockaddr_in serversin;
bzero(&serversin, sizeof(serversin));
serversin.sin_family = AF_INET;
serversin.sin_port = htons(80); //端口默认是80
memcpy(&serversin.sin_addr, ent->h_addr, ent->h_length);
//连接网页服务器
int myfd = socket(AF_INET, SOCK_STREAM, 0);
int res = connect(myfd, (struct sockaddr *)&serversin, sizeof(serversin));
if(res < 0)
{
perror("connect");
return;
}
printf("连接成功\n");
//将替换后的头部发给服务器
write(myfd, buffernew, strlen(buffernew));
//从网页服务器读回结果
int len;
while(read(myfd, buffer, 1) > 0) //如果大于0,表示有数据
{ //此处处理二进制
write(fd, buffer, 1); //客户端fd
}
}
int main()
{
//1 创建监听套接字,等待客户端(浏览器)的连接
int listenfd = create_listensocket();
//2 接受客户端的连接,然后去处理连接
while(1) //单线运行比较慢
{
//从监听套接字复制出新的fd描述符,去分别和客户端连接
int clientfd = accept(listenfd, NULL, NULL);
//处理
if(fork()==0) //新进程
{
handleclient(clientfd);
close(clientfd);
exit(0);
}
}
}
运行结果:
在浏览器中输入www.163.com 然后回车