上篇文章中做了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;
-
}