背景
问题:HTTP服务器在内网,终端在另一个内网,需要终端可以访问HTTP服务器。
解决:在公网布置一个TCP代理,HTTP服务器上也布置一个代理(只能内网主动访问外网)。
1. IP分片、TCP流和应用层消息提取
- UDP发大包是否会IP分片?
会。IP层进行重组。UDP头部8字节(源端口,目的端口,长度,校验和),比较简单。
- TCP发大包?
SYN报文中会携带MSS,后续流的发送过程中,超过MSS的报文会进行分段传输。
- 应用层消息提取?
如果在一个tcp长连接中连续发送多个应用层消息,则承载的应用层协议需要能够划分消息。tcp面向流,socket接收的可能是两个拼在一起的消息。应用层需要能够将流拆分成独立的消息。
2. socket option
setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #当一个有相同地址和端口的socket处于TIME_WAIT状态时,新socket要占用该地址和端口
setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) #socket保活打开
setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 10) #首次探测前TCP空闲时间
setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 30) #发送保活报文的间隔
setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 3) #判定断开前的探测次数
3. Python Tcp reverse proxy
流程:
实现:ServerA & ServerB
#!/usr/bin/env python |
C proxy client
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <netinet/tcp.h> #include <pthread.h>
#define PROXY_PORT 10099 #define RESTFUL_PORT 5000 #define BUFFER_SIZE 1024
int proxy_cli,restful_cli;
void close_sockets() { if(0 != shutdown(proxy_cli,SHUT_RDWR)){ //perror("shutdown proxy sock error"); }else{ printf("shutdown proxy\n"); }
if(0 != close(proxy_cli)){ //perror("close proxy sock error"); }else{ printf("close proxy\n"); }
if(0 != shutdown(restful_cli,SHUT_RDWR)){ //perror("shutdown restful sock error"); }else{ printf("shutdown restful\n"); }
if(0 != close(restful_cli)){ //perror("close restful sock error"); }else{ printf("close restful\n"); } }
void proxy_handler() { char recbuf[BUFFER_SIZE]; int cnt; int sendcnt; while(1){ cnt = recv(proxy_cli, recbuf, BUFFER_SIZE,0); if(cnt > 0) { //正常处理数据 printf("recv from proxy %d\n",cnt); sendcnt = send(restful_cli, recbuf, cnt,0);
if (sendcnt > 0){ printf("send to restful %d\n",sendcnt); } else{ printf("send to restful error %d\n",sendcnt); break; } } else { if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR)) { printf("proxy cnt < 0 and error:%d\n",errno); continue;//继续接收数据 }
if (0 == cnt){ printf("proxy recv close.\n"); } else{ printf("proxy recv:%d\n",cnt); }
break;//跳出接收循环 } } printf("proxy close sockets\n"); close_sockets(); return; }
void restful_handler() { char recbuf[BUFFER_SIZE]; int cnt; int sendcnt; while(1){ cnt = recv(restful_cli, recbuf, BUFFER_SIZE,0); if(cnt > 0) { //正常处理数据 printf("recv from restful %d\n",cnt); sendcnt = send(proxy_cli, recbuf, cnt,0);
if (sendcnt > 0){ printf("send to proxy %d\n",sendcnt); } else{ printf("send to proxy error %d\n",sendcnt); break; } } else { if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR)) { printf("restful cnt < 0 and error:%d\n",errno); continue;//继续接收数据 }
if(0==cnt){ printf("restful close\n"); }else{ printf("restful recv:%d\n",cnt); } break;//跳出接收循环 } } printf("restful close sockets\n"); close_sockets(); return; }
void setSocketOpt(int sk) { int keepalive = 1; // 开启keepalive属性 int keepidle = 6; // 如该连接在6秒内没有任何数据往来,则进行探测 int keepinterval = 5; // 探测时发包的时间间隔为5 秒 int keepcount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发. setsockopt(sk,SOL_SOCKET,SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive )); setsockopt(sk,SOL_TCP,TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle )); setsockopt(sk,SOL_TCP,TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval )); setsockopt(sk,SOL_TCP,TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount )); }
int main(int argc,char **argv) { if (1 == argc){ printf("usage: proxyproxy proxy_ip\n"); return 0; }else{ printf("proxy ip:%s\n",argv[1]); } pthread_t thread1, thread2; int tmp1, tmp2; struct sockaddr_in proxyaddr,restfuladdr; memset(&proxyaddr, 0, sizeof(proxyaddr)); proxyaddr.sin_family = AF_INET; proxyaddr.sin_port = htons(PROXY_PORT); proxyaddr.sin_addr.s_addr = inet_addr(argv[1]);
memset(&restfuladdr, 0, sizeof(restfuladdr)); restfuladdr.sin_family = AF_INET; restfuladdr.sin_port = htons(RESTFUL_PORT); restfuladdr.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1) { printf("**********************************************\n"); proxy_cli = socket(AF_INET,SOCK_STREAM, 0); ///连接服务器,成功返回0,错误返回-1 if (connect(proxy_cli, (struct sockaddr *)&proxyaddr, sizeof(proxyaddr)) < 0) { perror("connect proxy error"); sleep(5); continue; } setSocketOpt(proxy_cli);
restful_cli = socket(AF_INET,SOCK_STREAM, 0); if (connect(restful_cli, (struct sockaddr *)&restfuladdr, sizeof(restfuladdr)) < 0) { perror("connect restful error"); shutdown(proxy_cli,SHUT_RDWR); close(proxy_cli); sleep(5); continue; } setSocketOpt(restful_cli);
int ret_thrd1, ret_thrd2;
ret_thrd1 = pthread_create(&thread1, NULL, (void *)&proxy_handler, NULL); ret_thrd2 = pthread_create(&thread2, NULL, (void *)&restful_handler, NULL);
// 线程创建成功,返回0,失败返回失败号 if (ret_thrd1 != 0) { printf("create proxy thread error\n"); close_sockets(); } else { printf("create proxy thread\n"); }
if (ret_thrd2 != 0) { printf("create restful thread error\n"); close_sockets(); } else { printf("create restful thread\n"); }
//同样,pthread_join的返回值成功为0 tmp1 = pthread_join(thread1, NULL); //printf("thread1 return value(tmp) is %d\n", tmp1); if (tmp1 != 0) { printf("cannot join with thread1\n"); } //printf("thread1 end\n");
tmp2 = pthread_join(thread2, NULL); //printf("thread2 return value(tmp) is %d\n", tmp1); if (tmp2 != 0) { printf("cannot join with thread2\n"); } //printf("thread2 end\n"); printf("------------------------------------------------\n"); }
return 0; } |
延伸:
两个客户端之间创建隧道?