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;
}

文章目录