背景
問題: HTTPのサーバー、ネットワークを含むが、ネットワーク内の他の端末では、端末がアクセスする必要があるかもしれHTTPのサーバーを。
ソリューション:パブリックネットワークの配置のTCP プロキシ、HTTPのサーバも(外部ネットワークにアクセスするために、ネットワーク内のみ有効)プロキシを配置されています。
1. IPのフラグメンテーション、TCPのストリームとアプリケーション層メッセージ抽出
- UDPは、かどうか、大きなパケットを送信IPの断片化?
ウィル。IPの層の組換え体。UDP ヘッダ8 バイト(元ポート、宛先ポート、長さ、およびチェックサム)、比較的簡単です。
- TCPは大きなパッケージを送信しますか?
SYNのパケットが搬送するMSSをかけて、その後のプロセスストリームを送信し、MSSのパケットが送信を断片化することができます。
- アプリケーション層はメッセージを抽出しますか?
場合 TCP 長い連続送信に接続された複数のアプリケーション層のメッセージ、アプリケーション層プロトコルは、パーティショニング・メッセージを実施することができる必要とされます。TCP ストリーム指向、ソケットは、2件のメッセージが一緒にプッシュすることができる受信しました。アプリケーション層は、別個のメッセージ内に流入できるようにする必要があります。
2. ソケットオプション
setsockopt(1 socket.SOL_SOCKET、socket.SO_REUSEADDR) #の同じアドレスおよびポートソケットにTIME_WAITの状態は、新しいソケットアドレスとポートのが占有します
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のリバースプロキシ
プロセス:
実現:サーバーAサーバーB&
#!/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; } |
延伸:
两个客户端之间创建隧道?