NAT 관통 기술 상세 해석 (udp 구멍 뚫 기 정수 부 코드)

10753 단어 udpnat
예전 에 자신 이 쓴 코드 는 모두 현지에서 c / s 통신 만 했 는데 오늘 은 외부 네트워크 를 뛰 어 넘 을 수 있 는 c / s 통신 을 쓰 고 싶 습 니 다. 여기 서 저 는 udp 로 점 대 점 의 서로 다른 외부 네트워크 의 통신 을 실현 하 겠 습 니 다.사용 하 는 기술 은 nat 관통 기술 입 니 다. 여기 서 가장 직접 사용 하 는 것 은 udp 구멍 뚫 기 기술 입 니 다.글 에 표현 이 명확 하지 않 으 면 질문 을 환영 합 니 다.
만약 당신 이 nat 관통 기술 의 상세 한 해석 점 이 필요 하 다 면 여기: nat 관통 분석
필요 한 장치:
알 고 있 는 외부 네트워크 서버 S (ip + port), 서로 다른 외부 네트워크 에 있 는 클 라 이언 트 A, B
우선 udp 가 구멍 을 뚫 는 절 차 를 알 아야 합 니 다.
1. A 클 라 이언 트 는 S, B 클 라 이언 트 는 서버 S 에 메 시 지 를 보낸다.
2. S 전송 A 의 ip + port (여 기 는 A 의 외부 네트워크 ip + port, nat 전송 기 에 있 음) 클 라 이언 트 B, S 전송 B 의 ip + port 를 클 라 이언 트 A 에 게 전달 합 니 다.
이렇게 A, B 는 모두 엔 드 의 ip + port 를 알 게 되 었 다.
3. A 가 B 에 게 메 시 지 를 보 내 면 B 는 이 메 시 지 를 차단 하지만 A 의 nat 맵 에 맵 을 추가 하여 A 가 B 로부터 메 시 지 를 받 을 수 있 도록 합 니 다.A 에 구멍 을 뚫다
B-->A。
4. B 는 A 에 게 메 시 지 를 보 냅 니 다. 여 기 는 절차 3A 가 이 메 시 지 를 받 을 수 있 기 때문에 B 의 nat 맵 에 맵 을 추가 하여 B 가 A 에서 온 메 시 지 를 받 을 수 있 도록 합 니 다.B 에서
구멍 뚫 기, A - > B.
5. 여기까지 A, B 홀 성공.
저 는 이 지식 을 공부 할 때 인터넷 의 사례 와 해석 을 보면 대부분이 서로 전달 되 고 표현 이 잘 되 지 않 습 니 다. 코드 인 스 턴 스 를 가 져 와 서 사용 하 는 것 도 문제 가 많 고 실제 문 제 를 해결 하지 못 하기 때문에 자신 도 엄 격 히 요구 하고 문제 가 있 는 코드 를 붙 이지 못 합 니 다.
다음은 코드 입 니 다. A 반사 B 의 소식 을 실현 합 니 다.
서버 S 의 코드:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//      A      ip port      B,     B   ip port   A
//B  A       A,   A  B   ,  A nat     B   ,     
//   B             A 
//  ,  A  B       B,     B    A   ,          A
//       B   ,  A B        。  p2p
/*           S     AB        ,      A S    B S     ,      udp  。 */

#define ERR_EXIT(m)\
    do{\
        perror(m);\
        exit(1);\
    }while(0)

/*               ip+port */
typedef struct{
    struct in_addr ip;
    int port;
}clientInfo;

int main()
{
    /*             ,            ip+port */
    clientInfo info[2];
    /*                */
    /* char ch; */ 
    char str[10] = {0};

    /* udp socket    */
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)
        ERR_EXIT("SOCKET");

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    serveraddr.sin_port = htons(8888);
    serveraddr.sin_family = AF_INET;    

    int ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if(ret == -1)
        ERR_EXIT("BIND");

    /*                  */
    while(1)
    {
        bzero(info, sizeof(clientInfo)*2);
        /*                 ip+port */
        socklen_t addrlen = sizeof(struct sockaddr_in);
        /* recvfrom(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&serveraddr, &addrlen); */
        recvfrom(sockfd, str, sizeof(str), 0, (struct sockaddr *)&serveraddr, &addrlen);
        memcpy(&info[0].ip, &serveraddr.sin_addr, sizeof(struct in_addr));
        info[0].port = serveraddr.sin_port;

        printf("A client IP:%s \tPort:%d creat link OK!
", inet_ntoa(info[0].ip), ntohs(info[0].port)); /* recvfrom(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&serveraddr, &addrlen); */ recvfrom(sockfd, str, sizeof(str), 0, (struct sockaddr *)&serveraddr, &addrlen); memcpy(&info[1].ip, &serveraddr.sin_addr, sizeof(struct in_addr)); info[1].port = serveraddr.sin_port; printf("B client IP:%s \tPort:%d creat link OK!
", inet_ntoa(info[1].ip), ntohs(info[1].port)); /* ip+port */ printf("start informations translation...
"); serveraddr.sin_addr = info[0].ip; serveraddr.sin_port = info[0].port; sendto(sockfd, &info[1], sizeof(clientInfo), 0, (struct sockaddr *)&serveraddr, addrlen); serveraddr.sin_addr = info[1].ip; serveraddr.sin_port = info[1].port; sendto(sockfd, &info[0], sizeof(clientInfo), 0, (struct sockaddr *)&serveraddr, addrlen); printf("send informations successful!
"); } return 0; }

클 라 이언 트 A 의 코드:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*           */
#define ERR_EXIT(m)\
    do{\
        perror(m); \
        exit(1);\
    }while(0)

typedef struct{
    struct in_addr ip;
    int port;
}clientInfo;

/*   udp                 */
void echo_ser(int sockfd, struct sockaddr* addr, socklen_t *len)
{   
    printf("start recv B data...
"); char buf[1024]; while(1) { bzero(buf, sizeof(buf)); // B recvfrom(sockfd, buf, sizeof(buf)-1, 0, addr, len); printf("%s
", buf); // B printf("send data to B ...
"); sendto(sockfd, buf, sizeof(buf)-1, 0, addr, sizeof(struct sockaddr_in)); buf[strlen(buf)] = '\0'; if(strcmp(buf, "exit") == 0) break; } } int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd == -1) ERR_EXIT("SOCKET"); // char ch = 'a'; clientInfo info; socklen_t addrlen = sizeof(struct sockaddr_in); bzero(&info, sizeof(info)); struct sockaddr_in clientaddr; memset(&clientaddr, 0, sizeof(clientaddr)); // clientaddr.sin_port = htons(8888); // ip , ip , ip 。 clientaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); clientaddr.sin_family = AF_INET; /* S */ sendto(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&clientaddr, sizeof(struct sockaddr_in)); /* B ip+port */ printf("send success
"); recvfrom(sockfd, &info, sizeof(clientInfo), 0, (struct sockaddr *)&clientaddr, &addrlen); printf("IP: %s\tPort: %d
", inet_ntoa(info.ip), ntohs(info.port)); clientaddr.sin_addr = info.ip; clientaddr.sin_port = info.port; sendto(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&clientaddr, sizeof(struct sockaddr_in)); echo_ser(sockfd, (struct sockaddr *)&clientaddr, &addrlen); close(sockfd); return 0; }

클 라 이언 트 B 코드:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*           */
#define ERR_EXIT(m)\
    do{\
        perror(m); \
        exit(1);\
    }while(0)

typedef struct{
    struct in_addr ip;
    int port;
}clientInfo;

/*   udp                 */
void echo_ser(int sockfd, struct sockaddr* addr, socklen_t *len)
{   
    char buf[1024];
    while(1)
    {
        bzero(buf, sizeof(buf));
        printf(">> ");
        fflush(stdout);
        fgets(buf, sizeof(buf)-1, stdin);
        // A    
        sendto(sockfd, buf, strlen(buf), 0, addr, sizeof(struct sockaddr_in));

        //  A     
        bzero(buf, sizeof(buf));
        printf("start recv A data...
"); recvfrom(sockfd, buf, sizeof(buf)-1, 0, addr, len); printf("%s
", buf); buf[strlen(buf)] = '\0'; if(strcmp(buf, "exit") == 0) break; } } int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd == -1) ERR_EXIT("SOCKET"); // char ch = 'a'; /* char str[] = "abcdefgh"; */ clientInfo info; socklen_t addrlen = sizeof(struct sockaddr_in); bzero(&info, sizeof(info)); struct sockaddr_in clientaddr, serveraddr; /* ip+port */ /* memset(&clientaddr, 0, sizeof(clientaddr)); */ /* clientaddr.sin_port = htons(8888); */ /* clientaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */ /* clientaddr.sin_family = AF_INET; */ /* */ memset(&clientaddr, 0, sizeof(clientaddr)); // port serveraddr.sin_port = htons(4399); // ip, ip , ip ip serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /* clientaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */ serveraddr.sin_family = AF_INET; /* S */ sendto(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)); /* sendto(sockfd, str, sizeof(str), 0, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)); */ /* B ip+port */ printf("send success
"); recvfrom(sockfd, &info, sizeof(clientInfo), 0, (struct sockaddr *)&serveraddr, &addrlen); printf("IP: %s\tPort: %d
", inet_ntoa(info.ip), ntohs(info.port)); serveraddr.sin_addr = info.ip; serveraddr.sin_port = info.port; sendto(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)); echo_ser(sockfd, (struct sockaddr *)&serveraddr, &addrlen); close(sockfd); return 0; }

상기 코드 는 본인 의 디 버 깅 테스트 를 거 친 후에 크로스 오 버 네트워크 통신 을 실현 할 수 있 고 아무런 문제 가 없습니다.
이 항목 을 쓸 때 발생 하 는 문제:
1. 처음에 제 로 컬 클 라 이언 트 는 서버 S 에 연결 되 지 않 았 습 니 다. 코드 가 로 컬 테스트 에 성공 한 상황 에서 도 제 클 라 우 드 서버 가 지정 한 감청 포트 8888 이 열 리 지 않 고 외부 메 시 지 를 받 지 않 았 기 때 문 입 니 다.포트 를 열 어 성공 적 으로 해결 하 다.
2. 서버 S 는 AB 의 연결 메 시 지 를 받 을 수 있 고 AB 의 ip 과 port 를 상대방 에 게 전달 할 수 있 으 며 AB 도 상대방 의 ip + port 를 얻 을 수 있 습 니 다. 그러나 B 가 A 에 게 메 시 지 를 보 낼 때 A 는 while (1) 순환 중의 첫 번 째 recvfrom 에 막 혔 습 니 다. 왜 일 까요?
이 유 는 구멍 을 뚫 는 과정 에서 제 가 절차 3, 4 가 빠 졌 기 때 문 입 니 다. B 가 A 에 게 메 시 지 를 보 내 면 A 가 B 의 메 시 지 를 차단 하기 때 문 입 니 다.이때 A 도 B 에 게 메 시 지 를 보 내야 한다.
그래서 저 는 원래 A, B 코드 였 어 요.
echo_ser(sockfd, (struct sockaddr *)&serveraddr, &addrlen);

앞 에 한 마디 를 덧 붙 였 다.
 sendto(sockfd, &ch, sizeof(ch), 0, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in));

이렇게 하면 A 든 B 든 이 문 구 를 먼저 집행 하면 구멍 을 뚫 는 데 성공 할 수 있다. 만약 에 이 문장 앞 에 sleep (5) 를 한 마디 넣 으 면 구멍 을 뚫 는 과정 을 느 낄 수 있다.
여기까지 문 제 를 해결 하고 기대 하 는 기능 을 실현 하 였 으 니 나의 글 이 너 에 게 도움 이 되 기 를 바란다.표현 이 명확 하지 않 으 면 질문 을 환영 합 니 다.

좋은 웹페이지 즐겨찾기