├── client.c ├── server.c ├── 客户端说明.txt ├── 服务端说明.txt └── 运行实例.txt /client.c: -------------------------------------------------------------------------------- 1 | /*文件:client.c 2 | PS:第一个连接上服务器的客户端,称为client1,第二个连接上服务器的客户端称为client2 3 | 这个程序的功能是:先连接上服务器,根据服务器的返回决定它是client1还是client2, 4 | 若是client1,它就从服务器上得到client2的IP和Port,连接上client2, 5 | 若是client2,它就从服务器上得到client1的IP和Port和自身经转换后的port, 6 | 在尝试连接了一下client1后(这个操作会失败),然后根据服务器返回的port进行监听。 7 | 这样以后,就能在两个客户端之间进行点对点通信了。 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAXLINE 128 21 | #define SERV_PORT 8877 22 | 23 | typedef struct 24 | { 25 | char ip[32]; 26 | int port; 27 | }server; 28 | 29 | //发生了致命错误,退出程序 30 | void error_quit(const char *str) 31 | { 32 | fprintf(stderr, "%s", str); 33 | //如果设置了错误号,就输入出错原因 34 | if( errno != 0 ) 35 | fprintf(stderr, " : %s", strerror(errno)); 36 | printf("\n"); 37 | exit(1); 38 | } 39 | 40 | int main(int argc, char **argv) 41 | { 42 | int i, res, port; 43 | int connfd, sockfd, listenfd; 44 | unsigned int value = 1; 45 | char buffer[MAXLINE]; 46 | socklen_t clilen; 47 | struct sockaddr_in servaddr, sockaddr, connaddr; 48 | server other; 49 | 50 | if( argc != 2 ) 51 | error_quit("Using: ./client "); 52 | 53 | //创建用于链接(主服务器)的套接字 54 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 55 | memset(&sockaddr, 0, sizeof(sockaddr)); 56 | sockaddr.sin_family = AF_INET; 57 | sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 58 | sockaddr.sin_port = htons(SERV_PORT); 59 | inet_pton(AF_INET, argv[1], &sockaddr.sin_addr); 60 | //设置端口可以被重用 61 | setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 62 | 63 | //连接主服务器 64 | res = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); 65 | if( res < 0 ) 66 | error_quit("connect error"); 67 | 68 | //从主服务器中读取出信息 69 | res = read(sockfd, buffer, MAXLINE); 70 | if( res < 0 ) 71 | error_quit("read error"); 72 | printf("Get: %s", buffer); 73 | 74 | //若服务器返回的是first,则证明是第一个客户端 75 | if( 'f' == buffer[0] ) 76 | { 77 | //从服务器中读取第二个客户端的IP+port 78 | res = read(sockfd, buffer, MAXLINE); 79 | sscanf(buffer, "%s %d", other.ip, &other.port); 80 | printf("ff: %s %d\n", other.ip, other.port); 81 | 82 | //创建用于的套接字 83 | connfd = socket(AF_INET, SOCK_STREAM, 0); 84 | memset(&connaddr, 0, sizeof(connaddr)); 85 | connaddr.sin_family = AF_INET; 86 | connaddr.sin_addr.s_addr = htonl(INADDR_ANY); 87 | connaddr.sin_port = htons(other.port); 88 | inet_pton(AF_INET, other.ip, &connaddr.sin_addr); 89 | 90 | //尝试去连接第二个客户端,前几次可能会失败,因为穿透还没成功, 91 | //如果连接10次都失败,就证明穿透失败了(可能是硬件不支持) 92 | while( 1 ) 93 | { 94 | static int j = 1; 95 | res = connect(connfd, (struct sockaddr *)&connaddr, sizeof(connaddr)); 96 | if( res == -1 ) 97 | { 98 | if( j >= 10 ) 99 | error_quit("can't connect to the other client\n"); 100 | printf("connect error, try again. %d\n", j++); 101 | sleep(1); 102 | } 103 | else 104 | break; 105 | } 106 | 107 | strcpy(buffer, "Hello, world\n"); 108 | //连接成功后,每隔一秒钟向对方(客户端2)发送一句hello, world 109 | while( 1 ) 110 | { 111 | res = write(connfd, buffer, strlen(buffer)+1); 112 | if( res <= 0 ) 113 | error_quit("write error"); 114 | printf("send message: %s", buffer); 115 | sleep(1); 116 | } 117 | } 118 | //第二个客户端的行为 119 | else 120 | { 121 | //从主服务器返回的信息中取出客户端1的IP+port和自己公网映射后的port 122 | sscanf(buffer, "%s %d %d", other.ip, &other.port, &port); 123 | 124 | //创建用于TCP协议的套接字 125 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 126 | memset(&connaddr, 0, sizeof(connaddr)); 127 | connaddr.sin_family = AF_INET; 128 | connaddr.sin_addr.s_addr = htonl(INADDR_ANY); 129 | connaddr.sin_port = htons(other.port); 130 | inet_pton(AF_INET, other.ip, &connaddr.sin_addr); 131 | //设置端口重用 132 | setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 133 | 134 | //尝试连接客户端1,肯定会失败,但它会在路由器上留下记录, 135 | //以帮忙客户端1成功穿透,连接上自己 136 | res = connect(sockfd, (struct sockaddr *)&connaddr, sizeof(connaddr)); 137 | if( res < 0 ) 138 | printf("connect error\n"); 139 | 140 | //创建用于监听的套接字 141 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 142 | memset(&servaddr, 0, sizeof(servaddr)); 143 | servaddr.sin_family = AF_INET; 144 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 145 | servaddr.sin_port = htons(port); 146 | //设置端口重用 147 | setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 148 | 149 | //把socket和socket地址结构联系起来 150 | res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 151 | if( -1 == res ) 152 | error_quit("bind error"); 153 | 154 | //开始监听端口 155 | res = listen(listenfd, INADDR_ANY); 156 | if( -1 == res ) 157 | error_quit("listen error"); 158 | 159 | while( 1 ) 160 | { 161 | //接收来自客户端1的连接 162 | connfd = accept(listenfd,(struct sockaddr *)&sockaddr, &clilen); 163 | if( -1 == connfd ) 164 | error_quit("accept error"); 165 | 166 | while( 1 ) 167 | { 168 | //循环读取来自于客户端1的信息 169 | res = read(connfd, buffer, MAXLINE); 170 | if( res <= 0 ) 171 | error_quit("read error"); 172 | printf("recv message: %s", buffer); 173 | } 174 | close(connfd); 175 | } 176 | } 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiyu666/Penetrate_NAT_TCP/4222566fd8dd6984047130f235b6768fdc07891e/server.c -------------------------------------------------------------------------------- /客户端说明.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiyu666/Penetrate_NAT_TCP/4222566fd8dd6984047130f235b6768fdc07891e/客户端说明.txt -------------------------------------------------------------------------------- /服务端说明.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiyu666/Penetrate_NAT_TCP/4222566fd8dd6984047130f235b6768fdc07891e/服务端说明.txt -------------------------------------------------------------------------------- /运行实例.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiyu666/Penetrate_NAT_TCP/4222566fd8dd6984047130f235b6768fdc07891e/运行实例.txt --------------------------------------------------------------------------------