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
}

  

 

 

posted on   TanZhiWei  阅读(574)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
历史上的今天:
2022-08-01 WPF 开发遇到的问题
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示