Frpc 内网穿透客户端配置教程
github: https://github.com/fatedier/frp/releases
1 下载操作系统对应版本的Frpc.exe 客户端程序
2 配置对应的frpc.ini文件
3 切换到Frpc.exe目录,cmd执行:Frpc.exe -c frpc.ini
例如:frpc.ini 如下
例如 frps服务地址:10.10.10.10 端口10000
本地服务端口5000, frps 域名219801a6865241m00017testappe.frp.ws.com,端口8000
本地应用唯一实例:[219801a6865241m00017testapp]
假设本地Api为:http://192.169.137.10/5000/edit;postId=18337057#postBody
内网穿透代理为:http://219801a6865241m00017testappe.frp.ws.com/8000/edit;postId=18337057#postBody
注意:frpc.ini 配置通过请求后台下发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /// <summary> /// 内网穿透代理客户端配置 /// </summary> public class FrpcIni { public CommonSection Common { get ; set ; } public AppSection App { get ; set ; } } /// <summary> /// 公共配置节点 /// </summary> public class CommonSection { public string ServerAddress { get ; set ; } public string ServerPort { get ; set ; } public string AuthenticationMethod { get ; set ; } public string Token { get ; set ; } public string DialServerTimeout { get ; set ; } public string LoginFailExit { get ; set ; } } /// <summary> /// 应用配置 /// </summary> public class AppSection { public string Type { get ; set ; } public string RemotePort { get ; set ; } public string LocalIp { get ; set ; } public string LocalPort { get ; set ; } public string UseCompression { get ; set ; } public string CustomDomains { get ; set ; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /// <summary> /// 代理客户端Ini配置常量 /// </summary> internal class FrpcIniConst { //与服务端连接段和Key public const string CommonSection = "common" ; public const string ServerAddrKey = "server_addr" ; public const string ServerPortKey = "server_port" ; public const string AuthenticationMethodKey = "authentication_method" ; public const string TokenKey = "token" ; public const string DialServerTimeout = "dial_server_timeout" ; public const string LoginFailExit = "login_fail_exit" ; //单应用段和Key public const string AppSection = "app_name" ; public const string TypeKey = "type" ; public const string LocalIpKey = "local_ip" ; public const string LocalPortKey = "local_port" ; public const string UseCompressionKey = "use_compression" ; public const string CustomDomainsKey = "custom_domains" ; public const string RemotePortKey = "remote_port" ; public const string FrpcName = "frpc" ; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | /// <summary> /// 代理配置操作 /// </summary> internal class FrpcConfigurationOperation { /// <summary> /// 配置操作 /// </summary> /// <param name="responseRegister">注册响应的包</param> /// <param name="localServicePort">本地服务端口号</param> /// <param name="appSection">应用节点</param> public FrpcConfigurationOperation(ResponseRegister responseRegister, int localServicePort, string appSection) { _responseRegister = responseRegister; _localServicePort = localServicePort; _appSection = appSection; } /// <summary> /// 更新配置 /// </summary> /// <param name="frpcIniPath"></param> public void UpDateFrpcIni( string frpcIniPath) { var writeFrpcIni = ResponseToFrpcIni(_responseRegister, _localServicePort, _appSection); var readFrpcIni = ReadFrpcIniFromFile(frpcIniPath, _appSection); WriteFrpcIniToFile(frpcIniPath, writeFrpcIni, readFrpcIni, _appSection); } /// <summary> /// 响应包转换配置 /// </summary> /// <returns></returns> public FrpcIni ResponseToFrpcIni() { return ResponseToFrpcIni(_responseRegister, _localServicePort, _appSection); } /// <summary> /// 响应包转换配置 /// </summary> /// <param name="responseRegister"></param> /// <param name="localServicePort"></param> /// <param name="appSection"></param> /// <returns></returns> private FrpcIni ResponseToFrpcIni(ResponseRegister responseRegister, int localServicePort, string appSection) { if (responseRegister.Server == null ) throw new InvalidOperationException( "无法获取服务信息" ); if (!responseRegister.SubDomainList.Any()) throw new InvalidOperationException( "无法获取分配的子域" ); var app = responseRegister.SubDomainList.First(s => s.Subdomain.StartsWith(appSection, StringComparison.CurrentCultureIgnoreCase)); if (app == null ) throw new InvalidOperationException( "无法获取分配的子域" ); var frpcIni = new FrpcIni { Common = new CommonSection { ServerAddress = responseRegister.Server.ServerAddress, ServerPort = responseRegister.Server.ServerPort, AuthenticationMethod = responseRegister.Server.AuthenticationMethod, Token = responseRegister.Server.Token, DialServerTimeout = responseRegister.Server.DialServerTimeout, LoginFailExit = responseRegister.Server.LoginFailExit, }, App = new AppSection() }; frpcIni.App.Type = app.Type; frpcIni.App.RemotePort = app.RemotePort; frpcIni.App.UseCompression = app.UseCompression; //必须小写,否则路由无法找到:frp已知问题 frpcIni.App.CustomDomains = app.CustomDomain; frpcIni.App.LocalIp = LocalIp; frpcIni.App.LocalPort = localServicePort.ToString(); return frpcIni; } /// <summary> /// 从文件读取代理客户端配置 /// </summary> /// <param name="filePath"></param> /// <param name="appSection"></param> /// <returns></returns> private FrpcIni ReadFrpcIniFromFile( string filePath, string appSection) { if (!File.Exists(filePath)) return null ; DeleteErrorSection(filePath, appSection); return new FrpcIni { Common = new CommonSection { ServerAddress = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.ServerAddrKey), ServerPort = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.ServerPortKey), AuthenticationMethod = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.AuthenticationMethodKey), Token = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.TokenKey), DialServerTimeout = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.DialServerTimeout), LoginFailExit = IniHelper.Read(filePath, FrpcIniConst.CommonSection, FrpcIniConst.LoginFailExit) }, App = new AppSection { Type = IniHelper.Read(filePath, appSection, FrpcIniConst.TypeKey), LocalIp = IniHelper.Read(filePath, appSection, FrpcIniConst.LocalIpKey), LocalPort = IniHelper.Read(filePath, appSection, FrpcIniConst.LocalPortKey), UseCompression = IniHelper.Read(filePath, appSection, FrpcIniConst.UseCompressionKey), CustomDomains = IniHelper.Read(filePath, appSection, FrpcIniConst.CustomDomainsKey), RemotePort = IniHelper.Read(filePath, appSection, FrpcIniConst.RemotePortKey) } }; } /// <summary> /// 向文件写入代理客户端配置 /// </summary> /// <param name="filePath"></param> /// <param name="writeFrpcIni"></param> /// <param name="readFrpcIni"></param> /// <param name="appSection"></param> private void WriteFrpcIniToFile( string filePath, FrpcIni writeFrpcIni, FrpcIni readFrpcIni, string appSection) { if (IsNeedWrite(writeFrpcIni.Common.ServerAddress, readFrpcIni.Common.ServerAddress)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.ServerAddrKey, writeFrpcIni.Common.ServerAddress); if (IsNeedWrite(writeFrpcIni.Common.ServerPort, readFrpcIni.Common.ServerPort)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.ServerPortKey, writeFrpcIni.Common.ServerPort); if (IsNeedWrite(writeFrpcIni.Common.AuthenticationMethod, readFrpcIni.Common.AuthenticationMethod)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.AuthenticationMethodKey, writeFrpcIni.Common.AuthenticationMethod); if (IsNeedWrite(writeFrpcIni.Common.Token, readFrpcIni.Common.Token)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.TokenKey, writeFrpcIni.Common.Token); if (IsNeedWrite(writeFrpcIni.Common.DialServerTimeout, readFrpcIni.Common.DialServerTimeout)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.DialServerTimeout, writeFrpcIni.Common.DialServerTimeout); if (IsNeedWrite(writeFrpcIni.Common.LoginFailExit, readFrpcIni.Common.LoginFailExit)) IniHelper.Write(filePath, FrpcIniConst.CommonSection, FrpcIniConst.LoginFailExit, writeFrpcIni.Common.LoginFailExit); if (IsNeedWrite(writeFrpcIni.App.Type, readFrpcIni.App.Type)) IniHelper.Write(filePath, appSection, FrpcIniConst.TypeKey, writeFrpcIni.App.Type); if (IsNeedWrite(writeFrpcIni.App.LocalIp, readFrpcIni.App.LocalIp)) IniHelper.Write(filePath, appSection, FrpcIniConst.LocalIpKey, writeFrpcIni.App.LocalIp); if (IsNeedWrite(writeFrpcIni.App.LocalPort, readFrpcIni.App.LocalPort)) IniHelper.Write(filePath, appSection, FrpcIniConst.LocalPortKey, writeFrpcIni.App.LocalPort); if (IsNeedWrite(writeFrpcIni.App.UseCompression, readFrpcIni.App.UseCompression)) IniHelper.Write(filePath, appSection, FrpcIniConst.UseCompressionKey, writeFrpcIni.App.UseCompression); if (IsNeedWrite(writeFrpcIni.App.CustomDomains, readFrpcIni.App.CustomDomains, false )) IniHelper.Write(filePath, appSection, FrpcIniConst.CustomDomainsKey, writeFrpcIni.App.CustomDomains); if (IsNeedWrite(writeFrpcIni.App.RemotePort, readFrpcIni.App.RemotePort, false )) IniHelper.Write(filePath, appSection, FrpcIniConst.RemotePortKey, writeFrpcIni.App.RemotePort); } /// <summary> /// 需要写入 /// </summary> /// <param name="writeValue"></param> /// <param name="readValue"></param> /// <param name="ignoreCase"></param> /// <returns></returns> private bool IsNeedWrite( string writeValue, string readValue, bool ignoreCase = true ) { if ( string .IsNullOrWhiteSpace(writeValue)) return true ; if (ignoreCase) return !writeValue.Equals(readValue, StringComparison.CurrentCultureIgnoreCase); return !writeValue.Equals(readValue); } /// <summary> /// 删除错误节点 /// </summary> /// <param name="filePath"></param> /// <param name="appSection"></param> private void DeleteErrorSection( string filePath, string appSection) { var sections = IniHelper.ReadSections(filePath); if (sections == null || sections.Count <= 0) return ; var ignoreSection = FrpcIniConst.CommonSection; foreach ( var section in sections) { if (section.Equals(ignoreSection) || section.Equals(appSection)) continue ; IniHelper.DeleteSection(filePath, section); } } private const string LocalIp = "localhost" ; private readonly ResponseRegister _responseRegister; private readonly int _localServicePort; private readonly string _appSection; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /// <summary> /// 代理状态 /// </summary> public enum FrpcAgentStatus { [Description( "未知状态" )] UnKnow = -1, [Description( "代理开启成功" )] StartUpSuccess =0, [Description( "代理开启失败" )] StartUpFailed = 1, [Description( "代理关闭" )] Closed = 2, [Description( "代理路由冲突" )] Conflict = 3, [Description( "代理被拒绝连接" )] Refused =4, } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | /// <summary> /// 代理内网穿透客户端 /// </summary> public class FrpcAgent { /// <summary> /// 连接改变 /// </summary> public event EventHandler< bool > ConnectedChanged; /// <summary> /// 代理接收信息 /// </summary> public event EventHandler< string > OutputReceived; /// <summary> /// 代理收错误信息 /// </summary> public event EventHandler< string > ErrorReceived; /// <summary> /// 代理状态 /// </summary> public event EventHandler<FrpcAgentStatus> AgentStatus; public FrpcAgent() { Application.Current.Dispatcher?.Invoke(() => { Application.Current.Exit += Current_Exit; }); } /// <summary> /// 退出代理 /// </summary> public void Exit() { Application.Current.Dispatcher?.Invoke(() => { Application.Current.Exit -= Current_Exit; }); Current_Exit( null , null ); } /// <summary> /// 开启代理 /// </summary> /// <returns></returns> public async Task Start( string intranetPenetrationPath) { await Task.Run(() => { _frpcAgentFullName = Path.Combine(intranetPenetrationPath, $ "{FrpcIniConst.FrpcName}.exe" ); KillLocalMainModule(_frpcAgentFullName); StartProcess(_frpcAgentFullName, AgentIniParameter(), intranetPenetrationPath); }); } #region 私有方法 private void StartProcess( string projectUrl, string arguments, string intranetPenetrationPath) { ExitedHandle(); _frpcProcess = new Process { StartInfo = { UseShellExecute = false , WorkingDirectory=intranetPenetrationPath, FileName = projectUrl, Arguments = arguments, CreateNoWindow = true , WindowStyle = ProcessWindowStyle.Hidden }, EnableRaisingEvents = true }; //守护重启 _frpcProcess.Exited += FrpcProcess_Exited; _frpcProcess.StartInfo.RedirectStandardInput = true ; _frpcProcess.StartInfo.RedirectStandardOutput = true ; _frpcProcess.StartInfo.RedirectStandardError = true ; _frpcProcess.StartInfo.StandardErrorEncoding = Encoding.UTF8; _frpcProcess.StartInfo.StandardOutputEncoding = Encoding.UTF8; _frpcProcess.OutputDataReceived += FrpcProcess_OutputDataReceived; _frpcProcess.ErrorDataReceived += FrpcProcess_ErrorDataReceived; _frpcProcess.Start(); _frpcProcess.BeginOutputReadLine(); _frpcProcess.BeginErrorReadLine(); } private void FrpcProcess_ErrorDataReceived( object sender, DataReceivedEventArgs e) { if (! string .IsNullOrWhiteSpace(e.Data)) ErrorReceived?.Invoke(sender, $ "{FrpcIniConst.FrpcName} -ErrorDataReceived: {e.Data}" ); } private void FrpcProcess_OutputDataReceived( object sender, DataReceivedEventArgs e) { var strOutput = e.Data; if ( string .IsNullOrWhiteSpace(strOutput)) return ; var agentStatus = GetAgentStatus(strOutput); switch (agentStatus) { case FrpcAgentStatus.StartUpSuccess: ConnectedChanged?.Invoke( this , true ); break ; case FrpcAgentStatus.StartUpFailed: ConnectedChanged?.Invoke( this , false ); break ; case FrpcAgentStatus.Closed: ConnectedChanged?.Invoke( this , false ); break ; case FrpcAgentStatus.Conflict: ConnectedChanged?.Invoke( this , false ); KillLocalMainModule(_frpcAgentFullName); AgentStatus?.Invoke(sender, FrpcAgentStatus.Conflict); break ; case FrpcAgentStatus.Refused: ConnectedChanged?.Invoke( this , false ); Exit(); AgentStatus?.Invoke(sender, FrpcAgentStatus.Refused); break ; case FrpcAgentStatus.UnKnow: break ; default : throw new ArgumentOutOfRangeException(); } OutputReceived?.Invoke(sender, $ "{FrpcIniConst.FrpcName} -OutputDataReceived: {strOutput}" ); } private void FrpcProcess_Exited( object sender, EventArgs e) { ExitedHandle(); } private void ExitedHandle() { if (_frpcProcess == null ) return ; _frpcProcess.Exited -= FrpcProcess_Exited; ConnectedChanged?.Invoke( this , false ); OutputReceived?.Invoke( this , $ "{FrpcIniConst.FrpcName} -Exited:" ); _frpcProcess.Dispose(); _frpcProcess = null ; } /// <summary> /// 返回代理状态 /// </summary> /// <param name="strOutput"></param> /// <returns></returns> private FrpcAgentStatus GetAgentStatus( string strOutput) { var success = strOutput.ToLower().Contains(Proxy) && strOutput.ToLower().Contains(ProxySuccess); if (success) return FrpcAgentStatus.StartUpSuccess; var error = strOutput.ToLower().Contains(Proxy) && strOutput.ToLower().Contains(ProxyError); if (error) return FrpcAgentStatus.StartUpFailed; var close = strOutput.ToLower().Contains(Closing) || strOutput.ToLower().Contains(Closed) || strOutput.ToLower().Contains(Failed); if (close) return FrpcAgentStatus.Closed; var conflict = strOutput.ToLower().Contains(Conflict); if (conflict) return FrpcAgentStatus.Conflict; var refused = strOutput.ToLower().Contains(Refused); if (refused) return FrpcAgentStatus.Refused; return FrpcAgentStatus.UnKnow; } /// <summary> /// 杀掉当前应用域下的代理客户端 /// </summary> private void KillLocalMainModule( string agentFullName) { Process[] services = null ; try { services = Process.GetProcessesByName(FrpcIniConst.FrpcName); if (services.Length <= 0) return ; foreach ( var service in services) { if (service.MainModule != null && service.MainModule.FileName.StartsWith(agentFullName, StringComparison.CurrentCultureIgnoreCase)) service.Kill(); } } catch (Exception e) { ErrorReceived?.Invoke( null , e.Message); } finally { if (services != null ) { foreach ( var process in services) { process.Dispose(); } } } } private void Current_Exit( object sender, ExitEventArgs e) { try { if (_frpcProcess != null ) { _frpcProcess.Exited -= FrpcProcess_Exited; _frpcProcess.OutputDataReceived -= FrpcProcess_OutputDataReceived; _frpcProcess.ErrorDataReceived -= FrpcProcess_ErrorDataReceived; } _frpcProcess?.Kill(); _frpcProcess?.Close(); _frpcProcess?.Dispose(); _frpcProcess = null ; } catch (Exception ex) { ErrorReceived?.Invoke( null , ex.Message); } } /// <summary> /// 代理配置 /// </summary> private string AgentIniParameter() => $ "-c {FrpcIniConst.FrpcName}.ini" ; #endregion #region 私有字段 //代理开启成功 private const string Proxy = "proxy" ; private const string ProxySuccess = "success" ; //代理开启失败 private const string ProxyError = "error" ; private const string Failed = "failed" ; //断开 private const string Closing = "closing" ; private const string Closed = "closed" ; //冲突 private const string Conflict = "conflict" ; private const string Refused = "refused" ; //代理客户端完整路径 private string _frpcAgentFullName; private Process _frpcProcess; #endregion } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
2022-08-01 WPF 开发遇到的问题