|
2 | 2 | #include "log.h" |
3 | 3 | #include "git_version.h" |
4 | 4 | #include "fd_manager.h" |
| 5 | +#define DOMAIN_MAX_LEN 64 |
| 6 | +// 跨平台多进程支持 |
| 7 | +#ifdef _WIN32 |
| 8 | + #include <windows.h> |
| 9 | +#else |
| 10 | + #include <unistd.h> |
| 11 | + #include <sys/wait.h> |
| 12 | +#endif |
5 | 13 |
|
6 | 14 | using namespace std; |
7 | 15 |
|
@@ -1059,6 +1067,107 @@ int unit_test() |
1059 | 1067 | return 0; |
1060 | 1068 | } |
1061 | 1069 |
|
| 1070 | +int resolve_ipv6(char *domain, char *addr) { |
| 1071 | + struct addrinfo hints, *res; |
| 1072 | + int err; |
| 1073 | + |
| 1074 | +#ifdef _WIN32 |
| 1075 | + WSADATA wsaData; |
| 1076 | + // 初始化 Winsock |
| 1077 | + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { |
| 1078 | + fprintf(stderr, "WSAStartup failed\n"); |
| 1079 | + return -1; |
| 1080 | + } |
| 1081 | +#endif |
| 1082 | + |
| 1083 | + // 清空 hints 结构体并设置为查询 IPv6 |
| 1084 | + memset(&hints, 0, sizeof(hints)); |
| 1085 | + hints.ai_family = AF_INET6; // 设置为 IPv6 |
| 1086 | + |
| 1087 | + // 调用 getaddrinfo 进行解析 |
| 1088 | + err = getaddrinfo(domain, NULL, &hints, &res); |
| 1089 | + if (err != 0) { |
| 1090 | + fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err)); |
| 1091 | +#ifdef _WIN32 |
| 1092 | + WSACleanup(); // 清理 Winsock |
| 1093 | +#endif |
| 1094 | + return -1; |
| 1095 | + } |
| 1096 | + |
| 1097 | + // 将解析出的 IPv6 地址转换为字符串格式 |
| 1098 | + if (res != NULL) { |
| 1099 | + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr; |
| 1100 | + inet_ntop(AF_INET6, &ipv6->sin6_addr, addr, INET6_ADDRSTRLEN); |
| 1101 | + } |
| 1102 | + |
| 1103 | + // 释放内存 |
| 1104 | + freeaddrinfo(res); |
| 1105 | + |
| 1106 | +#ifdef _WIN32 |
| 1107 | + WSACleanup(); // 清理 Winsock |
| 1108 | +#endif |
| 1109 | + |
| 1110 | + return 0; |
| 1111 | +} |
| 1112 | + |
| 1113 | +#ifdef _WIN32 |
| 1114 | +// Windows 平台的子进程创建函数 |
| 1115 | +void create_child_process(const char *program_name, const char *addr, u32_t port, HANDLE job_handle, HANDLE hPipeWrite) { |
| 1116 | + // 创建命令行参数 |
| 1117 | + char cmd[256]; |
| 1118 | + snprintf(cmd, sizeof(cmd), "%s -l 127.0.0.1:%d -r [%s]:%d -u", program_name, port, addr, port); |
| 1119 | + |
| 1120 | + // 转换为宽字符 |
| 1121 | + wchar_t wcmd[256]; |
| 1122 | + MultiByteToWideChar(CP_UTF8, 0, cmd, -1, wcmd, 256); |
| 1123 | + |
| 1124 | + // 设置启动信息,重定向子进程的 stdout 到管道的写端 |
| 1125 | + STARTUPINFOW si = {sizeof(si)}; |
| 1126 | + PROCESS_INFORMATION pi = {0}; |
| 1127 | + si.hStdOutput = hPipeWrite; // 将子进程的 stdout 重定向到管道 |
| 1128 | + si.dwFlags |= STARTF_USESTDHANDLES; |
| 1129 | + |
| 1130 | + // 启动子进程 |
| 1131 | + if (!CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { |
| 1132 | + fprintf(stderr, "Failed to create process for port %d. Error: %lu\n", port, GetLastError()); |
| 1133 | + exit(1); |
| 1134 | + } |
| 1135 | + |
| 1136 | + // 将子进程添加到 Job Object 中 |
| 1137 | + if (!AssignProcessToJobObject(job_handle, pi.hProcess)) { |
| 1138 | + fprintf(stderr, "Failed to assign process to job object. Error: %lu\n", GetLastError()); |
| 1139 | + exit(1); |
| 1140 | + } |
| 1141 | + |
| 1142 | + // 确保子进程句柄关闭,避免资源泄露 |
| 1143 | + CloseHandle(pi.hProcess); |
| 1144 | + CloseHandle(pi.hThread); |
| 1145 | +} |
| 1146 | +#else |
| 1147 | +// Unix/Linux 平台的子进程创建函数 |
| 1148 | +void create_child_process(const char *program_name, const char *addr,int port) { |
| 1149 | + // 未实现!!! |
| 1150 | + // 相信会玩Linux的也不差这点命令执行功底吧。。 |
| 1151 | + pid_t pid = fork(); |
| 1152 | + if (pid == -1) { |
| 1153 | + perror("fork failed"); |
| 1154 | + exit(1); |
| 1155 | + } else if (pid == 0) { |
| 1156 | + // 子进程 |
| 1157 | + printf("Child process started for port %s\n", port); |
| 1158 | + char * argv = malloc(sizeof(char)*1024); |
| 1159 | + sprintf(argv,"%s -l 127.0.0.1:%d -r [%s]:%d -u",program_name,port,port,addr,port); |
| 1160 | + execlp(program_name, program_name, port, NULL); |
| 1161 | + // 如果 execlp 返回,说明失败 |
| 1162 | + perror("execlp failed"); |
| 1163 | + exit(1); |
| 1164 | + } else { |
| 1165 | + // 父进程保存子进程 PID,以便稍后终止 |
| 1166 | + printf("Child process PID: %d\n", pid); |
| 1167 | + } |
| 1168 | +} |
| 1169 | +#endif |
| 1170 | + |
1062 | 1171 | int main(int argc, char *argv[]) |
1063 | 1172 | { |
1064 | 1173 | init_ws(); |
@@ -1089,9 +1198,86 @@ int main(int argc, char *argv[]) |
1089 | 1198 | assert(sizeof(i32_t)==4); |
1090 | 1199 | dup2(1, 2); //redirect stderr to stdout |
1091 | 1200 | int i, j, k; |
1092 | | - process_arg(argc,argv); |
1093 | | - |
1094 | | - event_loop(); |
1095 | | - |
1096 | | - return 0; |
| 1201 | + |
| 1202 | + // 定义默认参数,你可以在这里完成你的自定义,比如默认域名或端口 |
| 1203 | + const char * defaultDomain = "kore.host"; |
| 1204 | + u32_t port1 = 10999; |
| 1205 | + u32_t port2 = 10998; |
| 1206 | + // start -------------- |
| 1207 | + char *domain; |
| 1208 | + // 中文输出 设置控制台编码UTF-8 |
| 1209 | + SetConsoleOutputCP(CP_UTF8); |
| 1210 | + if(argc <3){ // 主进程逻辑 |
| 1211 | + puts("----------*****----------"); |
| 1212 | + puts("这是一个在原版(wangyu-/tinyPortMapper)基础上修改过的程序,目的是提供饥荒联机版的IPv6端口映射方式完成直连,以显著降低联机时延。\n"); |
| 1213 | + puts("你可以使用原版的参数列表,也可以使用快捷映射命令:./dstClientPortMapper.exe [domain] "); |
| 1214 | + puts("如果你双击直接运行(不输入参数),则将使用域名 \"kore.host\" 作为缺省参数。"); |
| 1215 | + puts("该命令将会把域名的10999和10998端口映射到本地127.0.0.1地址上。进入饥荒联机版以后可以按'~'唤出控制台,输入 c_connect(\"127.0.0.1\") 然后回车即可\n"); |
| 1216 | + puts("直接关闭窗口即可结束映射。感谢你的使用。"); |
| 1217 | + puts("如果有侵权问题,请联系我。https://github.com/binbla\n"); |
| 1218 | + puts("如果你想要完成自己的修改,自己读源码吧。😋\n"); |
| 1219 | + puts("----------*****----------"); |
| 1220 | + if(argc == 1){ |
| 1221 | + domain = (char*)malloc(sizeof(char)*DOMAIN_MAX_LEN); |
| 1222 | + strcpy(domain,defaultDomain);// 默认域名 |
| 1223 | + printf("use default domain: %s\n",defaultDomain); |
| 1224 | + }else{ |
| 1225 | + domain = argv[1]; |
| 1226 | + } |
| 1227 | + // 域名解析 |
| 1228 | + char addr[INET6_ADDRSTRLEN]; |
| 1229 | + if (resolve_ipv6(domain, addr) == 0) { |
| 1230 | + printf("IPv6 address for %s: %s\n", domain, addr); |
| 1231 | + } else { |
| 1232 | + printf("Failed to resolve IPv6 address for %s\n", domain); |
| 1233 | + exit(-1); |
| 1234 | + } |
| 1235 | + printf("Parent process started.\n"); |
| 1236 | +#ifdef _WIN32 //windows 逻辑 |
| 1237 | + // 创建一个 Job Object 来管理所有子进程 |
| 1238 | + HANDLE job_handle = CreateJobObject(NULL, NULL); |
| 1239 | + if (job_handle == NULL) { |
| 1240 | + fprintf(stderr, "Failed to create job object. Error: %lu\n", GetLastError()); |
| 1241 | + exit(1); |
| 1242 | + } |
| 1243 | + // 创建管道,父进程从中读取数据 |
| 1244 | + HANDLE hPipeRead, hPipeWrite; |
| 1245 | + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; |
| 1246 | + if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { |
| 1247 | + fprintf(stderr, "CreatePipe failed\n"); |
| 1248 | + exit(1); |
| 1249 | + } |
| 1250 | + // 创建子进程并将它们添加到 Job Object 中 |
| 1251 | + create_child_process(argv[0], addr, port1, job_handle, hPipeWrite); |
| 1252 | + create_child_process(argv[0], addr, port2, job_handle, hPipeWrite); |
| 1253 | + // 父进程从管道读取并输出 |
| 1254 | + CloseHandle(hPipeWrite); // 关闭管道的写端 |
| 1255 | + char buf[256]; |
| 1256 | + DWORD bytesRead; |
| 1257 | + while (ReadFile(hPipeRead, buf, sizeof(buf) - 1, &bytesRead, NULL) && bytesRead > 0) { |
| 1258 | + buf[bytesRead] = '\0'; // 添加 null 终止符 |
| 1259 | + printf("%s", buf); // 打印子进程的输出 |
| 1260 | + } |
| 1261 | + |
| 1262 | + // 父进程等待子进程退出 |
| 1263 | + WaitForSingleObject(job_handle, INFINITE); |
| 1264 | + // 清理 Job Object |
| 1265 | + CloseHandle(job_handle); |
| 1266 | +#else //unix 逻辑 |
| 1267 | + // 创建两个子进程 |
| 1268 | + create_child_process(argv[0], addr,port1); |
| 1269 | + sleep(1); |
| 1270 | + create_child_process(argv[0], addr,port2); |
| 1271 | + // 父进程等待所有子进程退出(通过发送信号终止) |
| 1272 | + // 父进程等待子进程(仅 Unix/Linux 需要,Windows 子进程生命周期与父进程绑定) |
| 1273 | + while (wait(NULL) > 0); // 等待所有子进程退出 |
| 1274 | +#endif |
| 1275 | + printf("Parent process exiting\n"); |
| 1276 | + return 0; |
| 1277 | + }else{// 子进程逻辑 |
| 1278 | + printf("Child process started\n"); |
| 1279 | + process_arg(argc,argv); |
| 1280 | + event_loop(); |
| 1281 | + return 0; |
| 1282 | + } |
1097 | 1283 | } |
0 commit comments