C语言写的内网穿透工具,将家用电脑的服务映射到公网。
公网服务器实现原理:
1.创建两个socket(两个不同的端口)分别监听私网服务器的连接;
2.等待私网服务器连接;
3.等待客户端连接,收到客户端的连接请求立即发送消息通知私网服务器
4.等待私网服务器发起连接
5.创建独立线程将客户端的数据与私网服务器的数据相互转发。
运行于公网程序源码
#include <stdio.h>
#include <windows.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define Link_server_port 4001 //监听服务器的端口
#define Link_user_port 4002 //监听用户(客户端)端口
//数据转发函数
void s1_s2(SOCKET sock1, SOCKET sock2);
//通道维护函数
void link_hello(SOCKET sock);
void link_chek(SOCKET sock);
int main()
{
//初始化网络库
WSADATA vs;
WSAStartup(MAKEWORD(2, 2), &vs);
//创建监听私网服务器socket与网络信息
SOCKET listen_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN listen_server_net;
listen_server_net.sin_family = AF_INET;
listen_server_net.sin_addr.S_un.S_addr = INADDR_ANY;
listen_server_net.sin_port = htons(4001);
//绑定socket与网络参数
if (::bind(listen_server, (SOCKADDR*)&listen_server_net, sizeof(listen_server_net)) == SOCKET_ERROR)
{
printf("绑定4001端口错误\n"); Sleep(2000); exit(-1);
}
//监听链接
if (listen(listen_server, 5) == SOCKET_ERROR)
{
printf("监听4001端口错误\n"); Sleep(2000); exit(-1);
}
//创建监听客户端socket与网络信息
SOCKET listen_user = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN listen_user_net;
listen_user_net.sin_family = AF_INET;
listen_user_net.sin_addr.S_un.S_addr = INADDR_ANY;
listen_user_net.sin_port = htons(4002);
//绑定socket与网络参数
if (::bind(listen_user, (SOCKADDR*)&listen_user_net, sizeof(listen_user_net)) == SOCKET_ERROR)
{
printf("绑定4002端口错误\n"); Sleep(2000); exit(-1);
}
//监听链接
if (listen(listen_user, 5) == SOCKET_ERROR)
{
printf("监听4002端口错误\n"); Sleep(2000); exit(-1);
}
//等待服务器连接
printf("等待内网服务器连接\n");
SOCKET to_server;
SOCKADDR_IN tmp;
int j = sizeof(tmp);
do
{
to_server = accept(listen_server, (SOCKADDR*)&tmp, &j);
} while (to_server <= 0);
printf("内网服务器连接成功:%s \n", inet_ntoa(tmp.sin_addr));
//通道维护
thread TD(link_hello, to_server);
TD.detach();
thread TD2(link_chek, to_server);
TD2.detach();
//循环等待用户链接
while (true)
{
SOCKET user = accept(listen_user, (SOCKADDR*)&tmp, &j);
if (user<0) continue;
printf("用户连接成功:%s\n", inet_ntoa(tmp.sin_addr));
//通知服务器有新的链接
send(to_server, "new_connet", 20, 0);
//等待服务器发起新链接
printf("等待服务器响应\n");
SOCKET server = accept(listen_server, NULL, NULL);
if (server < 0) continue;
printf("服务器已响应\n");
//两个socket加入到相互转发线程
thread t1(s1_s2, user, server);
t1.detach();
thread t2(s1_s2, server, user);
t2.detach();
}
closesocket(listen_server);
closesocket(listen_user);
WSACleanup();
return 0;
}
//数据转发函数
void s1_s2(SOCKET sock1, SOCKET sock2)
{
while (1)
{
char data[1024] = { 0 };
int ret = recv(sock1, data, 1024, 0);
if (ret > 0)
{
send(sock2, data, ret, 0);
}
if (ret == 0)
{
closesocket(sock1);
closesocket(sock2);
return;
}
if (ret < 0)
{
closesocket(sock1);
return;
}
}
}
//通道维护
void link_hello(SOCKET sock)
{
while (1)
{
send(sock, "hello pack", 11, 0);
Sleep(10000);
}
}
void link_chek(SOCKET sock)
{
while (1)
{
char data[128] = { 0 };
int ret = recv(sock, data, 128, 0);
if (ret < 1)
{
closesocket(sock);
WSACleanup();
exit(0);
}
}
}
私网服务器实现原理
1.创建socket连接公网服务器,等待新客户连接申请
2.收到新客户连接申请,向公网服务器发起新的连接
3.创建socket向本地服务端口发起连接
4.创建独立线程将服务端口数据与客户端数据相互转发
运行在内网服务器代码
#include <stdio.h>
#include <windows.h>
#include <string>
#include <thread>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define public_ip "103.222.189.25" //公网服务器IP
#define public_port 4001 //公网服务器端口
#define local_ip "127.0.0.1" //本地服务器IP
#define local_port 80 // 本地服务器端口
int num = 0;
//数据转发函数
void s1_s2(SOCKET sock1, SOCKET sock2);
int main()
{
//初始化网络参数
WSADATA vs;
WSAStartup(MAKEWORD(2, 2), &vs);
//创建链接公网服务的socket与网络信息
SOCKET listen_IP = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN IP_net;
IP_net.sin_family = AF_INET;
IP_net.sin_addr.S_un.S_addr = inet_addr(public_ip);
IP_net.sin_port = htons(public_port);
//链接公网服务器
if (connect(listen_IP, (SOCKADDR*)&IP_net, sizeof(IP_net)) == SOCKET_ERROR)
{
printf("链接公网服务器失败\n"); Sleep(2000); exit(-1);
}
printf("链接公网服务器成功\n");
//创建服务器信息
SOCKADDR_IN server_net;
server_net.sin_family = AF_INET;
server_net.sin_addr.S_un.S_addr = inet_addr(local_ip);
server_net.sin_port = htons(local_port);
//等待公网服务器发送消息
while (true)
{
while (true)
{
char data[128] = { 0 };
int ret = recv(listen_IP, data, 128, 0);
if (ret == 11 && !strcmp(data, "hello pack"))
{
send(listen_IP, "hello pack", 11, 0);
continue;
}
if (ret == 20 && !strcmp(data, "new_connet"))
{
printf("收到新用户链接请求\n");
//有新的用户链接,向公网服务器发起一个新的链接
SOCKET to_user = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connect(to_user, (SOCKADDR*)&IP_net, sizeof(IP_net)) == SOCKET_ERROR)
{
closesocket(to_user);
continue;
}
printf("向公网服务器发起新链接成功\n");
//向服务器发起链接
SOCKET to_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connect(to_server, (SOCKADDR*)&server_net, sizeof(server_net)) == SOCKET_ERROR)
{
printf("链接本机服务出错\n ");
closesocket(to_user);
continue;
}
printf("连接本机服务成功\n");
//数据转发线程
thread t1(s1_s2, to_user, to_server);
t1.detach();
thread t2(s1_s2, to_server, to_user);
t2.detach();
}
if (ret <= 0)
{
closesocket(listen_IP);
printf("服务器关闭连接\n");
Sleep(1000);
exit(0);
}
}
}
}
//数据转发函数
void s1_s2(SOCKET sock1, SOCKET sock2)
{
while (1)
{
char data[1024] = { 0 };
int ret = recv(sock1, data, 1024, 0);
if (ret > 0)
{
num += ret;
send(sock2, data, ret, 0);
}
if (ret == 0)
{
closesocket(sock1);
closesocket(sock2);
return;
}
if (ret < 0)
{
closesocket(sock1);
return;
}
}
}