http://blog.163.com/lixiangqiu_9202/blog/static/535750372015128113734761/
上篇文章中做了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;
-
}