tcp内网穿透
tcp内网穿透的思路是:
设服务器s,内网用户1,内网用户2
1连接服务器,服务器获取到1的公网ip和地址
2连接服务器,服务器获取到2的公网ip和地址
服务器告诉1,"你是第一个,第二个人的ip和port,快回去连接第二个人吧"。
服务器告诉2,"你是第二个,第一个人的ip和port,还有你自己的公网port,快回去监听这个公网port吧"。
用户1不断尝试连接2,
用户2先尝试连一次1,一定会失败,因为首先用户1没有在listen,并且在地址受限或端口受限的网络中,这样不是之 前主动连出去的ip+port的数据包会被丢弃,陌生的ip+port的数据包是不能进到1的网关中的。但是用户2先连接一次1,会在自己的网关上留下了一个去访问了1的ip+port的记录,2的网关认为2与1在2的ip+port上建立了连接,之后就允许1反向回来传数据了,此时正好有1在不断朝着这个ip+port发数据,这个数据包在这次注定失败的连接后,可以成功通过2的网关发给2了,现在2开始listen,这样就可以接受到1的connect,从而建立起tcp连接了。
getsockname是获取当前socket_fd的信息,存到一个sockadder_in中,其实就是获取当前socket_fd的ip和端口,这个函数经过我的测试,有的时候可以获得当前局域网的端口和ip,又有的时候可以获取到出去的公网ip和端口。看一看结果可以,但感觉没啥用。
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 可以将一个socket当前使用的端口进程端口重用,比如说当前socket_fd的端口是port_self,一会儿要用这个端口port_self再创建一个新的socket,用来listen,那么就需要使用setsockopt这个函数,将端口设置为可以重用。
服务器S.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#define PORT 65520
#ifdef _WIN32
#include <winsock2.h>
#include<ws2tcpip.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
typedef struct sockaddr SA;
typedef struct sockaddr_in SA_IN;
typedef struct {
struct in_addr ip;
int port;
}IP; //记录ip与端口
union A
{
u_short us;
char str[2];
};
void type_port(sockaddr_in addr)
{
sockaddr *newp = (struct sockaddr *)&addr;
A aa;
aa.str[0]=newp->sa_data[1];
aa.str[1]=newp->sa_data[0];
printf("%d\n", aa.us);
}
int main ( int argc, char **argv ) {
SA_IN server,addr;
int sockfd;
IP ip;
char s;
socklen_t addrlen = sizeof (SA_IN );
#ifdef _WIN32
//Winsows下启用socket
WSADATA wsadata;
if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR) {
printf("WSAStartup() fail\n");
exit(0);
}
#endif
sockfd=socket(AF_INET,SOCK_STREAM, 0);
if (sockfd==-1 ) {
perror ( "socket" );
return -1;
}
#ifdef _WIN32
memset(&server, 0, sizeof(SA_IN));
#else
bzero (&server, sizeof(SA_IN));
#endif
server.sin_port=htons (PORT);
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);
type_port(addr);
//接收两个心跳包
recv(newfd[0], &s, sizeof(char), 0);
printf("get from A %c\n", s);
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);
type_port(addr);
printf("get from B\n");
printf("%s\t%d OK\n", inet_ntoa (addr.sin_addr), ntohs(addr.sin_port));
send(newfd[0], (char *)&ip, sizeof(IP), 0);
send(newfd[1], (char *)&ip, sizeof(IP), 0);
#ifdef _WIN32
//Winsows下关闭socket
Sleep(3000);
closesocket(newfd[0]);
closesocket(newfd[1]);
WSACleanup();
#else
sleep(3);
close (newfd [ 0 ] );
close (newfd [ 1 ] );
#endif
break;
}
return 0;
}
客户端A.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#define SER "127.0.0.1"
#define PORT 65520
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
using namespace std;
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 ) {
memset(buf, 0, 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;
#ifdef _WIN32
//Winsows下启用socket
WSADATA wsadata;
if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR) {
printf("WSAStartup() fail\n");
exit(0);
}
#endif
sockfd =socket(AF_INET,SOCK_STREAM, 0);
memset(&server, 0, 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, (char *)&flags, sizeof(int)) == -1)
perror("setsockopt sockfd");
connect (sockfd, (SA *) &server, sizeof (SA_IN ));
send(sockfd, &s, sizeof (char), 0);
printf("i have already send my data\n");
recv(sockfd, (char *)&ip, sizeof (IP), 0);
// recv(sockfd, pp, sizeof (IP), 0 );
printf("%d\n", ntohs(ip.port));
printf("%s\n", inet_ntoa(ip.ip));
#ifdef _WIN32
//Winsows下关闭socket
closesocket(sockfd);
#else
close(sockfd);
#endif
sockfd2=socket(AF_INET, SOCK_STREAM, 0);
if(sockfd2 == -1)
{
printf("socket 创建失败\n");
}
if(setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR, (char *)&flags, sizeof(int))==-1)
{
printf("port reuse failed!\n");
}
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=ip.port;
printf("bind port is %d\n", ntohs(server.sin_port));
if(bind(sockfd2, (SA *)&server, sizeof(SA_IN))==-1)
{
printf("绑定失败!\n");
}
if(listen(sockfd2, 20)==-1)
{
printf("监听失败!\n");
}
// echo_ser(accept(sockfd2, (SA *)&addr, &addrlen));
int socket_id_B = accept(sockfd2, (SA *)&addr, &addrlen);
printf("socket_B_id is %d\n", socket_id_B);
echo_ser(socket_id_B);
// string data = "hello i am A";
// send(sockfd, &data.c_str(), data.size(), 0);
#ifdef _WIN32
//Winsows下关闭socket
closesocket(sockfd2);
WSACleanup();
#else
close(sockfd2);
#endif
return 0;
}
客户端B.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define SER "127.0.0.1"
#define PORT 65520
#ifdef _WIN32
#include <winsock2.h>
#include<ws2tcpip.h>
#include <windows.h>
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
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 ];
printf("speaking to A\n");
while (1) {
memset(buf, 0, sizeof(buf));
printf ( ">" );
fflush (stdout);
fgets (buf, sizeof (buf) -1,stdin );
send (sockfd,buf,strlen(buf), 0 );
memset(buf, 0, 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 );
#ifdef _WIN32
//Winsows下启用socket
WSADATA wsadata;
if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR) {
printf("WSAStartup() fail\n");
exit(0);
}
#endif
sockfd=socket(AF_INET,SOCK_STREAM, 0);
memset(&server, 0, 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));
printf("i am have already send my data\n");
recv(sockfd, (char *)&ip, sizeof(IP), 0);
printf("%d\n", ntohs(ip.port));
printf("%s\n", inet_ntoa(ip.ip));
#ifdef _WIN32
//Winsows下关闭socket
closesocket(sockfd);
#else
close(sockfd);
#endif
sockfd2 =socket (AF_INET,SOCK_STREAM, 0 );
server.sin_addr =ip.ip;
server.sin_port =ip.port;
int connect_status = connect(sockfd2, (SA *)&server, sizeof(SA_IN));
while ( connect_status != 0) {
printf("connecting!\n");
connect_status = connect(sockfd2, (SA *)&server, sizeof(SA_IN));
printf("status code is %d\n", connect_status);
printf("%d\n", ntohs(server.sin_port));
printf("%s\n", inet_ntoa(server.sin_addr));
#ifdef _WIN32
Sleep(3000);
#else
sleep(2);
#endif
}
echo_cli(sockfd2);
#ifdef _WIN32
//Winsows下关闭socket
closesocket(sockfd2);
WSACleanup();
#else
close(sockfd2);
#endif
return 0;
}