https://blog.csdn.net/soralaro/article/details/86696349
内网穿透 TCP打洞 C语言实现
时间 2017-03-29
原文 http://blog.csdn.net/wuzuyu365/article/details/68068133
上篇文章中做了UDP打洞,这篇当然就会是TCP打洞了,两个处于不同内网的两台机器如何通过TCP/IP协议进行链接通讯呢?这其实跟UDP打洞差不多,基本步骤是这个样子的。
假设我们有两台处于不同内网的两台机器A和B和一台众所周知外网IP的服务器S,而机器A中运行着通讯的服务端程序B运行着通讯的客户端程序,那么
1、A连接S,S记录A的外网IP与通讯的端口
2、B连接S
3、S将A与此通讯的端口号返回给A
4、S将A与此连接的IP与端口号返回给B
5、A在程序中将服务绑定并侦听在从S返回的端口
6、B使用从S返回的IP与端口连接A
这样A与B就成功连接了,这里需要注意的一点就是两个socket在同一个端口绑定的问题,socket提供了setsockopt函数,其中参数SO_REUSEADDR可以解决这个问题
下面是c语言代码示例
S中的程序
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <arpa/inet.h> #include <netinet/in.h> typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN; typedef struct { struct in_addr ip; int port; }IP; //记录ip与端口 int main(int argc,char **argv) { SA_IN server,addr; int sockfd; IP ip; char s; socklen_t addrlen=sizeof(SA_IN); sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd == -1) { perror("socket"); return -1; } bzero(&server,sizeof(SA_IN)); server.sin_port=htons(8888); server.sin_family=AF_INET; server.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(SA *)&server,sizeof(SA_IN)) == -1) { perror("bind"); return -1; } if(listen(sockfd,20) == -1) { perror("listen"); return -1; } while(1) { int newfd[2]; newfd[0]=accept(sockfd,(SA *)&addr,&addrlen); //接收两个心跳包 recv(newfd[0],&s,sizeof(char),0); memcpy(&ip.ip,&addr.sin_addr,sizeof(struct in_addr)); ip.port=addr.sin_port; printf("%s\t%d OK\n",inet_ntoa(ip.ip),ntohs(ip.port)); newfd[1]=accept(sockfd,(SA *)&addr,&addrlen); printf("%s\t%d OK\n", inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); send(newfd[0],&ip,sizeof(IP),0); send(newfd[1],&ip,sizeof(IP),0); close(newfd[0]); close(newfd[1]); } return 0; }
A中的程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #define SER "xxx.xxx.xxx.xxx" #define PORT 8888 typedef struct { struct in_addr ip; int port; }IP; //ip与端口 typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN; //回射服务 void echo_ser(int sockfd) { char buf[1024]; while(1) { bzero(buf,sizeof(buf)); //接收B发来的数据 recv(sockfd,buf,sizeof(buf)-1,0); printf("%s",buf); //向B发送数据 send(sockfd,buf,strlen(buf),0); buf[strlen(buf)-1]='\0'; if(strcmp(buf,"exit") == 0) break; } } int main(int argc,char **argv) { int sockfd,sockfd2; SA_IN server,addr; IP ip; socklen_t addrlen=sizeof(SA_IN); char s='a'; int flags=1; sockfd=socket(AF_INET,SOCK_STREAM,0); bzero(&server,sizeof(SA_IN)); server.sin_family=AF_INET; server.sin_addr.s_addr=inet_addr(SER); server.sin_port=htons(PORT); if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flags,sizeof(int)) == -1) perror("setsockopt sockfd"); connect(sockfd,(SA *)&server,sizeof(SA_IN)); send(sockfd,&s,sizeof(char),0); recv(sockfd,&ip,sizeof(IP),0); close(sockfd); sockfd2=socket(AF_INET,SOCK_STREAM,0); if(sockfd2 == -1) perror("sockfd2"); if(setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR,&flags,sizeof(int)) == -1) perror("setsockopt sockfd2"); server.sin_addr.s_addr=INADDR_ANY; server.sin_port=ip.port; if(bind(sockfd2,(SA *)&server,sizeof(SA_IN)) == -1) perror("bind sockfd"); if(listen(sockfd2,20) == -1) perror("listen"); echo_ser(accept(sockfd2,(SA *)&addr,&addrlen)); close(sockfd2); return 0; }
B中的程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #define SER "xxx.xxx.xxx.xxx" #define PORT 8888 typedef struct { struct in_addr ip; int port; }IP; //ip与端口 typedef struct sockaddr SA; typedef struct sockaddr_in SA_IN; void echo_cli(int sockfd) { char buf[1024]; while(1) { bzero(buf,sizeof(buf)); printf(">"); fflush(stdout); fgets(buf,sizeof(buf)-1,stdin); send(sockfd,buf,strlen(buf),0); bzero(buf,sizeof(buf)); recv(sockfd,buf,sizeof(buf)-1,0); printf("%s",buf); buf[strlen(buf)-1]='\0'; if(strcmp(buf,"exit") == 0) break; } } int main(int argc,char **argv) { int sockfd,sockfd2; SA_IN server,addr; IP ip; socklen_t addrlen=sizeof(SA_IN); sockfd=socket(AF_INET,SOCK_STREAM,0); bzero(&server,sizeof(SA_IN)); server.sin_family=AF_INET; server.sin_addr.s_addr=inet_addr(SER); server.sin_port=htons(PORT); connect(sockfd,(SA *)&server,sizeof(SA_IN)); recv(sockfd,&ip,sizeof(IP),0); close(sockfd); sockfd2=socket(AF_INET,SOCK_STREAM,0); server.sin_addr=ip.ip; server.sin_port=ip.port; while(connect(sockfd2,(SA *)&server,sizeof(SA_IN)) == -1) perror("connect"); echo_cli(sockfd2); close(sockfd2); return 0; }