免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 7899 | 回复: 5

最近补习socket课程,写了个端口转发程序。 [复制链接]

论坛徽章:
0
发表于 2010-05-23 21:19 |显示全部楼层
本帖最后由 lemboyz 于 2010-05-23 21:25 编辑

  1. #include <sys/socket.h>
  2. #include <netinet/in.h>
  3. #include <arpa/inet.h>
  4. #include <signal.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <errno.h>
  8. #include <iostream>
  9. using namespace std;

  10. #define flag 0
  11. #define cout if(flag == 1) cout << "[" << __FUNCTION__ << "()," << __FILE__ << "," << __LINE__ << "] "

  12. void set_keepalive(int fd);
  13. int Writen(int fd,const void* vptr, int n);
  14. int Client(const string& remote);  // remote: 12.34.56.78:90
  15. int Server(const string& local);   // local : 12.34.56.78:90
  16. void * trans(void *arg);
  17. void daemon_init();

  18. int main(int argc,char** argv)
  19. {
  20.         if(argc!=3) {
  21.                 cout << "usage : " << argv[0] << " localip:port remoteip:port" << endl;
  22.                 exit(-1);
  23.         }
  24.         int nRet;
  25.         string local = argv[1];
  26.         string remote = argv[2];

  27.         daemon_init();

  28.         int listenfd = Server(local);

  29.         int connfd,clientfd;
  30.         while(1) {
  31.                 connfd = accept(listenfd,(struct sockaddr*)NULL,NULL);
  32.                 if(connfd < 0) {
  33.                         cout << "accept() error," << strerror(errno) << endl;
  34.                         exit(errno);
  35.                 }
  36.                 clientfd = Client(remote);
  37.                 if(clientfd == -1) {
  38.                         close(connfd);
  39.                         continue;
  40.                 }
  41.                 set_keepalive(connfd);
  42.                 set_keepalive(clientfd);

  43.                 if(clientfd > 0) {
  44.                         if(fork()==0) {
  45.                                 cout << getpid() << " child start." << endl;
  46.                                 pthread_t p1,p2;
  47.                                 int conn[2];
  48.                                 int conn_[2];
  49.                                 conn[0] = connfd; conn[1] = clientfd;
  50.                                 conn_[0]= clientfd; conn_[1] = connfd;
  51.                                 pthread_create(&p1, NULL, trans, (void*)conn);
  52.                                
  53.                                 pthread_create(&p2, NULL, trans, (void*)conn_);
  54.                        
  55.                                 pthread_join(p2,NULL);
  56.                                 pthread_join(p1,NULL);
  57.                                 cout << getpid() << " child normally exit." << endl;
  58.                                 exit(0); // child exit
  59.                         }
  60.                        
  61.                         close(connfd);
  62.                         close(clientfd);
  63.                 }
  64.         }

  65. }

  66. void set_keepalive(int fd)
  67. {
  68.         int val = 1;
  69.         int nRet = setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&val,sizeof(val));
  70.         if(nRet < 0) {
  71.                 cout << "setsockopt(fd,SOL_SOCKET,SOL_KEEPALIVE,&val,sizeof(val)) error," << strerror(errno) << endl;
  72.                 return;
  73.         }

  74.         /*
  75.         val = 60;
  76.         nRet = setsockopt(fd,IPPROTO_TCP,TCP_KEEPALIVE,&val,sizeof(val));
  77.         if(nRet < 0) {
  78.                 cout << "setsockopt(fd,IPPROTO_TCP,TCP_KEEPALIVE,&val,sizeof(val)) error," << strerror(errno) << endl;
  79.                 return;
  80.         }
  81.         */

  82.         return;
  83. }

  84. void * trans(void *arg)
  85. {
  86.         int connfd1,connfd2;
  87.         connfd1 = ((int*)arg)[0];
  88.         connfd2 = ((int*)arg)[1];
  89.         cout << "connfd1=" << connfd1 << ", connfd2=" << connfd2 << endl;

  90.         int nRead, nWrite;
  91.         char buf[10240]={0};
  92.         while(1) {
  93.                 cout << getpid() <<" before read() ";
  94.                 nRead = read(connfd1,buf,sizeof(buf));
  95.                 cout << getpid() <<" read() return: " << nRead << endl;
  96.                 if(nRead < 0) {
  97.                         if(errno == EINTR) continue;
  98.                         cout << getpid() << " read() error," << strerror(errno) << endl;
  99.                         break;
  100.                 }
  101.                 if(nRead == 0) { // remote close
  102.                         break;
  103.                 }
  104.                
  105.                 nWrite = Writen(connfd2,buf,nRead);
  106.                 cout << getpid() << " Writen() return: " << nWrite << endl;
  107.                 if(nWrite < 0 || nWrite!=nRead) {
  108.                         cout << getpid() << " Writen() error." << endl;
  109.                         break;
  110.                 }
  111.         }
  112.         close(connfd1);
  113.         close(connfd2);

  114.         exit(0);
  115.         //pthread_exit(NULL);
  116. }

  117. int Client(const string& remote)
  118. {
  119.         int clientfd,nRet;
  120.         clientfd = socket(AF_INET,SOCK_STREAM,0);
  121.         if(clientfd < 0) {
  122.                 cout << "socket() error," << strerror(errno) << endl;
  123.                 return -1;
  124.         }
  125.         struct sockaddr_in addr;
  126.         bzero(&addr,sizeof(addr));

  127.         int pos;
  128.         string remoteip,remoteport;
  129.         if((pos = remote.find(':')) != string::npos) {
  130.                 remoteip = remote.substr(0,pos);
  131.                 remoteport = remote.substr(pos+1);
  132.         }
  133.         addr.sin_family = AF_INET;
  134.         addr.sin_port = htons(atoi(remoteport.c_str()));
  135.         if(inet_pton(AF_INET,remoteip.c_str(),&addr.sin_addr) <= 0) {
  136.                 cout << "inet_pton() error," << strerror(errno) << endl;
  137.                 return -1;
  138.         }

  139.         if(connect(clientfd,(struct sockaddr*)&addr,sizeof(addr)) < 0) {
  140.                 cout << "connect() error," << strerror(errno) << endl;
  141.                 return -1;
  142.         }

  143.         return clientfd;
  144. }

  145. int Server(const string& local)
  146. {
  147.         string localip,localport;
  148.         int pos;
  149.        
  150.         if((pos = local.find(':')) != string::npos) {
  151.                 localip = local.substr(0,pos);
  152.                 localport = local.substr(pos+1);
  153.                 cout << "localip["<< localip << "],localport[" << localport << "]" << endl;
  154.         }
  155.         int listenfd;
  156.         listenfd = socket(AF_INET,SOCK_STREAM,0);
  157.         if(listenfd < 0) {
  158.                 cout << "socket() error," << strerror(errno) << endl;
  159.                 exit(errno);
  160.         }
  161.         struct sockaddr_in srvaddr;
  162.         bzero(&srvaddr,sizeof(srvaddr));
  163.         srvaddr.sin_family = AF_INET;
  164.         srvaddr.sin_port = htons(atoi(localport.c_str()));
  165.        
  166.         int nRet = inet_pton(AF_INET,localip.c_str(),&srvaddr.sin_addr);
  167.         if(nRet < 0) {
  168.                 cout << "inet_pton() error," << strerror(errno) << endl;
  169.                 exit(errno);
  170.         }
  171.        
  172.         int val = 1;
  173.         nRet = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));
  174.         if(nRet < 0 ) {
  175.                 cout << "setsockopt() error," << strerror(errno) << endl;
  176.                 exit(errno);
  177.         }
  178.        
  179.         nRet = bind(listenfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr));
  180.         if(nRet < 0 ) {
  181.                 cout << "bind() error," << strerror(errno) << endl;
  182.                 exit(errno);
  183.         }
  184.         nRet = listen(listenfd,10);
  185.         if(nRet < 0) {
  186.                 cout << "listen() error," << strerror(errno) << endl;
  187.                 exit(errno);
  188.         }
  189.        
  190.         return listenfd;
  191. }

  192. void daemon_init()
  193. {
  194.         if(fork() != 0) {
  195.                 exit(0);
  196.         }
  197.         if(fork() != 0) {
  198.                 exit(0);
  199.         }

  200.         for(int i = 1; i<=64; i++) {
  201.                 signal(i,SIG_IGN);
  202.         }

  203.         setsid();
  204.         umask(0);
  205. }

  206. int Writen(int fd,const void* vptr, int n)
  207. {
  208.         int nleft;
  209.         int nwritten;
  210.         const char *ptr;
  211.         ptr = (char*)vptr;
  212.         nleft = n;

  213.         while(nleft > 0) {
  214.                 if( (nwritten = write(fd,ptr,nleft))  <= 0) {
  215.                         if(errno == EINTR)
  216.                                 nwritten = 0;
  217.                         else
  218.                                 return -1;
  219.                 }
  220.                 nleft -= nwritten;
  221.                 ptr += nwritten;
  222.         }

  223.         return n;
  224. }

复制代码
测试了一下,速度还可以。
现在还有个疑问,我建了两个线程分别处理向前和向后两个方向的转发,当其中一个线程read()返回0时,我知道是要关闭两端的fd了,但我如何通知另一个线程要退出了呢?

论坛徽章:
0
发表于 2010-05-23 21:54 |显示全部楼层
想要做到graceful exit你得把你的程序改成非阻塞的,然后就可以选很多种通知机制了。例如eventfd/pipe/信号量/消息队列等等N多方法。开销最小的是eventfd,不过要求比较新的内核支持。

不想改程序就exit()硬退吧。

论坛徽章:
0
发表于 2010-05-23 23:04 |显示全部楼层
我觉得对你的程序应该不需要通知,因为第一个线程read 0说明对方已关闭,另外一个线程write时肯定出错,这样两个线程都会关闭了,当然对于复杂的情况要用没本所说的一些通信方式了

论坛徽章:
0
发表于 2010-05-23 23:20 |显示全部楼层
回复 3# wwdwwd


    他的程序是类似TCP proxy或者说中转,两个线程连接的不是同一个目标,不会出现一个线程断开另外一个就write出错的情况。当然非阻塞下其实只用一个线程就可以完成要求。

论坛徽章:
0
发表于 2010-05-24 09:53 |显示全部楼层
我觉得对你的程序应该不需要通知,因为第一个线程read 0说明对方已关闭,另外一个线程write时肯定出错,这样 ...
wwdwwd 发表于 2010-05-23 23:04

对方是否关闭套接字,依赖于应用。部分应用需要使用TCP半关闭的特性。不会你没使用过半关闭吧。

论坛徽章:
0
发表于 2010-05-24 12:25 |显示全部楼层
对方是否关闭套接字,依赖于应用。部分应用需要使用TCP半关闭的特性。不会你没使用过半关闭吧。
pagx 发表于 2010-05-24 09:53
寒,没必要这么说吧。你看一下楼主的意思,他想要一个线程关闭时通知另一个线程,那就是说这两个线程是需要同时关闭的,也就是说他这里的tcp不会或者不处理半关闭,要不然A线程关闭跟B线程就没啥关系了,干嘛要通知B线程啊!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP