background
Problem: HTTP server, including network, in another terminal within the network, the terminal may need to access the HTTP server.
Solution: a public network arrangement TCP proxy, HTTP server is also arranged a proxy (only active within the network to access external network).
1. the IP fragmentation, the TCP stream and the application layer message extraction
- UDP send large packets whether IP fragmentation?
meeting. IP layer recombinant. UDP header 8 bytes (source port, destination port, length, and checksum), is relatively simple.
- TCP send large packages?
SYN packet carries MSS , transmitting the subsequent process stream, over MSS packets may be fragmented transmission.
- Application layer extracted message?
If a tcp more application layer message connected to long continuous transmission, the application layer protocol is required can be carried partitioning message. tcp stream oriented, Socket received two messages may be pushed together. The application layer needs to be able to flow into separate message.
2. socket option
setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # when a same address and port of the socket in the TIME_WAIT state, a new socket for the address and port occupies
setsockopt (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) #socket keepalive open
setsockopt (socket.SOL_TCP, socket.TCP_KEEPIDLE, 10) # for the first time before the probe TCP idle time
setsockopt (socket.SOL_TCP, socket.TCP_KEEPINTVL, 30) # send keepalive interval
setsockopt (socket.SOL_TCP, socket.TCP_KEEPCNT, 3) # determines the number of pre-break detection
3. Python Tcp reverse proxy
Process:
Realization: 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; } |
延伸:
两个客户端之间创建隧道?