上篇文章中作了UDP打洞,這篇固然就會是TCP打洞了,兩個處於不一樣內網的兩臺機器如何經過TCP/IP協議進行連接通信呢?這其實跟UDP打洞差很少,基本步驟是這個樣子的。
假設咱們有兩臺處於不一樣內網的兩臺機器A和B和一臺衆所周知外網IP的服務器S,而機器A中運行着通信的服務端程序B運行着通信的客戶端程序,那麼服務器
一、A鏈接S,S記錄A的外網IP與通信的端口
二、B鏈接S
三、S將A與此通信的端口號返回給A
四、S將A與此鏈接的IP與端口號返回給B
五、A在程序中將服務綁定並偵聽在從S返回的端口
六、B使用從S返回的IP與端口鏈接Asocket
這樣A與B就成功鏈接了,這裏須要注意的一點就是兩個socket在同一個端口綁定的問題,socket提供了setsockopt函數,其中參數SO_REUSEADDR能夠解決這個問題函數
下面是c語言代碼示例server
S中的程序ip
#include <stdio.h>get
#include <sys/socket.h>string
#include <sys/types.h>it
#include <stdlib.h>io
#include <string.h>cli
#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;
}