内网穿透 TCP打洞 【c语言实现】

上篇文章中做了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中的程序

  1. #include <stdio.h>

  2. #include <sys/socket.h>

  3. #include <sys/types.h>

  4. #include <stdlib.h>

  5. #include <string.h>

  6. #include <errno.h>

  7. #include <arpa/inet.h>

  8. #include <netinet/in.h>

  9.  

  10. typedef struct sockaddr SA;

  11. typedef struct sockaddr_in SA_IN;

  12.  

  13. typedef struct

  14. {

  15.  struct in_addr ip;

  16.  int port;

  17. }IP; //记录ip与端口

  18.  

  19. int main(int argc,char **argv)

  20. {

  21.  SA_IN server,addr;

  22.  int sockfd;

  23.  IP ip;

  24.  char s;

  25.  socklen_t addrlen=sizeof(SA_IN);

  26.  

  27.  sockfd=socket(AF_INET,SOCK_STREAM,0);

  28.  if(sockfd == -1)

  29.  {

  30.   perror("socket");

  31.   return -1;

  32.  }

  33.  bzero(&server,sizeof(SA_IN));

  34.  server.sin_port=htons(8888);

  35.  server.sin_family=AF_INET;

  36.  server.sin_addr.s_addr=INADDR_ANY;

  37.  if(bind(sockfd,(SA *)&server,sizeof(SA_IN)) == -1)

  38.  {

  39.   perror("bind");

  40.   return -1;

  41.  }

  42.  if(listen(sockfd,20) == -1)

  43.  {

  44.   perror("listen");

  45.   return -1;

  46.  }

  47.  

  48.  while(1)

  49.  {

  50.   int newfd[2];

  51.  

  52.   newfd[0]=accept(sockfd,(SA *)&addr,&addrlen);

  53.   //接收两个心跳包

  54.   recv(newfd[0],&s,sizeof(char),0);

  55.   memcpy(&ip.ip,&addr.sin_addr,sizeof(struct in_addr));

  56.   ip.port=addr.sin_port;

  57.   printf("%s\t%d OK\n",inet_ntoa(ip.ip),ntohs(ip.port));

  58.  

  59.   newfd[1]=accept(sockfd,(SA *)&addr,&addrlen);

  60.   printf("%s\t%d OK\n",

  61.     inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));

  62.  

  63.   send(newfd[0],&ip,sizeof(IP),0);

  64.   send(newfd[1],&ip,sizeof(IP),0);

  65.  

  66.   close(newfd[0]);

  67.   close(newfd[1]);

  68.  }

  69.  

  70.  return 0;

  71. }

A中的程序

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <string.h>

  4. #include <sys/socket.h>

  5. #include <sys/types.h>

  6. #include <arpa/inet.h>

  7.  

  8. #define SER "xxx.xxx.xxx.xxx"

  9. #define PORT 8888

  10.  

  11. typedef struct

  12. {

  13.  struct in_addr ip;

  14.  int port;

  15. }IP; //ip与端口

  16.  

  17. typedef struct sockaddr SA;

  18. typedef struct sockaddr_in SA_IN;

  19.  

  20. //回射服务

  21. void echo_ser(int sockfd)

  22. {

  23.  char buf[1024];

  24.  

  25.  while(1)

  26.  {

  27.   bzero(buf,sizeof(buf));

  28.   //接收B发来的数据

  29.   recv(sockfd,buf,sizeof(buf)-1,0);

  30.   printf("%s",buf);

  31.   //向B发送数据

  32.   send(sockfd,buf,strlen(buf),0);

  33.  

  34.   buf[strlen(buf)-1]='\0';

  35.   if(strcmp(buf,"exit") == 0)

  36.    break;

  37.  }

  38. }

  39.  

  40. int main(int argc,char **argv)

  41. {

  42.  int sockfd,sockfd2;

  43.  SA_IN server,addr;

  44.  IP ip;

  45.  socklen_t addrlen=sizeof(SA_IN);

  46.  char s='a';

  47.  int flags=1;

  48.  

  49.  sockfd=socket(AF_INET,SOCK_STREAM,0);

  50.  

  51.  bzero(&server,sizeof(SA_IN));

  52.  server.sin_family=AF_INET;

  53.  server.sin_addr.s_addr=inet_addr(SER);

  54.  server.sin_port=htons(PORT);

  55.  

  56.  if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flags,sizeof(int)) == -1)

  57.   perror("setsockopt sockfd");

  58.  connect(sockfd,(SA *)&server,sizeof(SA_IN));

  59.  send(sockfd,&s,sizeof(char),0);

  60.  recv(sockfd,&ip,sizeof(IP),0);

  61.  close(sockfd);

  62.  

  63.  sockfd2=socket(AF_INET,SOCK_STREAM,0);

  64.  if(sockfd2 == -1)

  65.   perror("sockfd2");

  66.  if(setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR,&flags,sizeof(int)) == -1)

  67.   perror("setsockopt sockfd2");

  68.  server.sin_addr.s_addr=INADDR_ANY;

  69.  server.sin_port=ip.port;

  70.  if(bind(sockfd2,(SA *)&server,sizeof(SA_IN)) == -1)

  71.   perror("bind sockfd");

  72.  if(listen(sockfd2,20) == -1)

  73.   perror("listen");

  74.  

  75.  echo_ser(accept(sockfd2,(SA *)&addr,&addrlen));

  76.  

  77.  close(sockfd2);

  78.  

  79.  return 0;

  80. }

B中的程序

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <string.h>

  4. #include <sys/socket.h>

  5. #include <sys/types.h>

  6. #include <arpa/inet.h>

  7.  

  8. #define SER "xxx.xxx.xxx.xxx"

  9. #define PORT 8888

  10.  

  11. typedef struct

  12. {

  13.  struct in_addr ip;

  14.  int port;

  15. }IP; //ip与端口

  16.  

  17. typedef struct sockaddr SA;

  18. typedef struct sockaddr_in SA_IN;

  19.  

  20. void echo_cli(int sockfd)

  21. {

  22.  char buf[1024];

  23.  

  24.  while(1)

  25.  {

  26.   bzero(buf,sizeof(buf));

  27.   printf(">");

  28.   fflush(stdout);

  29.   fgets(buf,sizeof(buf)-1,stdin);

  30.   send(sockfd,buf,strlen(buf),0);

  31.  

  32.   bzero(buf,sizeof(buf));

  33.   recv(sockfd,buf,sizeof(buf)-1,0);

  34.   printf("%s",buf);

  35.   buf[strlen(buf)-1]='\0';

  36.   if(strcmp(buf,"exit") == 0)

  37.    break;

  38.  }

  39. }

  40.  

  41. int main(int argc,char **argv)

  42. {

  43.  int sockfd,sockfd2;

  44.  SA_IN server,addr;

  45.  IP ip;

  46.  socklen_t addrlen=sizeof(SA_IN);

  47.  

  48.  sockfd=socket(AF_INET,SOCK_STREAM,0);

  49.  

  50.  bzero(&server,sizeof(SA_IN));

  51.  server.sin_family=AF_INET;

  52.  server.sin_addr.s_addr=inet_addr(SER);

  53.  server.sin_port=htons(PORT);

  54.  

  55.  connect(sockfd,(SA *)&server,sizeof(SA_IN));

  56.  recv(sockfd,&ip,sizeof(IP),0);

  57.  close(sockfd);

  58.  

  59.  sockfd2=socket(AF_INET,SOCK_STREAM,0);

  60.  server.sin_addr=ip.ip;

  61.  server.sin_port=ip.port;

  62.  while(connect(sockfd2,(SA *)&server,sizeof(SA_IN)) == -1)

  63.   perror("connect");

  64.  

  65.  echo_cli(sockfd2);

  66.  

  67.  close(sockfd2);

  68.  return 0;

  69. }

转载于:https://my.oschina.net/u/1777508/blog/1572550

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很高兴为您解答,关于用C语言实现内网穿透的代码,可以参考以下示例代码: #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> int main() { int sockfd, port; struct sockaddr_in serv_addr; struct hostent *server; int bytes_recvd; char buffer[1024]; // Create socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { printf("socket() failed!"); exit(1); } // Connect to server port = 80; server = gethostbyname("www.example.com"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("connect() failed!"); exit(1); } // Receive data bytes_recvd = recv(sockfd, buffer, 1024, 0); if (bytes_recvd < 0) { printf("recv() failed!"); exit(1); } else { buffer[bytes_recvd] = '\0'; printf("Data received: %s", buffer); } // Close socket close(sockfd); return 0; } ### 回答2: 内网穿透是一种技术,它通过将内网的数据包通过公网中转,实现内网与外网之间的通信。要用C语言实现内网穿透的代码,可以借助第三方库例如libevent来简化网络编程。 首先,我们需要建立一个服务器和客户端来连接内外网。服务器位于外网,并监听来自客户端的连接请求。客户端位于内网,连接到服务器,将内网数据包通过服务器转发出去。 服务器端代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <event2/event.h> void client_read_cb(struct bufferevent* bev, void* ctx) { struct evbuffer* input = bufferevent_get_input(bev); struct evbuffer* output = bufferevent_get_output(bev); size_t len = evbuffer_get_length(input); if (len > 0) { // 处理接收到的从客户端发送的数据 // 然后将数据发送给目标服务器 evbuffer_add_buffer(output, input); } } void server_accept_cb(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int socklen, void* ctx) { struct event_base* base = ctx; struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); // 设置客户端的读回调函数 bufferevent_setcb(bev, client_read_cb, NULL, NULL, NULL); bufferevent_enable(bev, EV_READ | EV_WRITE); } int main() { struct event_base* base; struct evconnlistener* listener; struct sockaddr_in server_addr; base = event_base_new(); if (!base) { printf("Could not initialize libevent!\n"); return -1; } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(1234); // 设置监听端口 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); listener = evconnlistener_new_bind(base, server_accept_cb, (void*)base, LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (!listener) { printf("Could not create a listener!\n"); return -1; } event_base_dispatch(base); evconnlistener_free(listener); event_base_free(base); return 0; } ``` 客户端代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <event2/event.h> void server_read_cb(struct bufferevent* bev, void* ctx) { struct evbuffer* input = bufferevent_get_input(bev); struct evbuffer* output = bufferevent_get_output(bev); size_t len = evbuffer_get_length(input); if (len > 0) { // 处理接收到的从服务器发送的数据 // 然后将数据发送给内网目标设备 evbuffer_add_buffer(output, input); } } int main() { struct event_base* base; struct bufferevent* bev; base = event_base_new(); if (!base) { printf("Could not initialize libevent!\n"); return -1; } bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); if (!bev) { printf("Could not create a buffer event!\n"); return -1; } struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = 0; // 任意可用的本地端口 if (bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin)) < 0) { bufferevent_free(bev); printf("Could not connect to the server!\n"); return -1; } bufferevent_setcb(bev, server_read_cb, NULL, NULL, NULL); bufferevent_enable(bev, EV_READ | EV_WRITE); event_base_dispatch(base); bufferevent_free(bev); event_base_free(base); return 0; } ``` 以上示例代码演示了使用C语言实现内网穿透的基本原理。实际应用中可能还需要处理各种网络异常和错误处理,具体实现代码应根据具体需求进行调整和完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值