内网穿透 TCP打洞 C语言实现

https://blog.csdn.net/soralaro/article/details/86696349

内网穿透 TCP打洞 C语言实现

时间  2017-03-29

标签 socket c语言 打洞 穿透 栏目 系统网络

原文   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;
} 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值