用 TortoiseGit 拉代码或推代码的时候,弹出一个红色报错框:“Network error: Connection refused”。这个场景我见过太多次了,说真的,第一次遇到时我也懵过——感觉网络明明是通的,浏览器也能打开网页,怎么 Git 就连接被拒了。网上搜一圈,翻来覆去是“检查网络”“重启电脑”“重新安装”这种万能废话,看完毫无帮助。今天我把这个报错拆开揉碎,从 TCP 握手原理到 TortoiseGit 自身的 SSH 设置,把可能的原因和排查路径一次讲清楚,希望能省下你半天排查时间。
这个报错的本质,是客户端在建立 TCP 连接时,目标主机主动拒绝了握手请求。翻译成大白话就是:你打电话过去,对方电话线没插好,或者压根没人接听。它跟“域名解析不出来(DNS 失败)”“连接超时(timed out)”完全不是一回事。先把这三者的区别搞清楚,排查方向就成功了一半。下面我按从网络层到客户端配置层的顺序,把整个排查过程完整过一遍。
1. 先弄明白:Connection refused 到底在说什么
1.1 一次被拒绝的 TCP 握手
Git 的所有远程操作(clone、push、pull),底层都要建立网络连接。TortoiseGit 表面上是个图形界面工具,但背后跑的仍然是 Git 命令行那套逻辑,只是把操作封装成了右键菜单和弹窗。当你点击“Pull”或“Push”时,程序会尝试连接远程仓库配置的主机和端口,而这个连接建立在 TCP 协议之上,也就是我们常说的“三次握手”。
正常的握手过程是:客户端发 SYN,服务端回 SYN-ACK,客户端再回 ACK,双方确认链路可用,然后就进入数据传输阶段。而 Connection refused 的出现,意味着客户端发出的 SYN 包得到了一个 RST(reset,重置)回应。RST 有两个常见来源:一是目标端口上根本没有进程在监听,操作系统直接帮你拒绝;二是中间有防火墙设备主动发了 RST,告诉你“此路不通”。无论是哪种,结果都一样——连接建立失败,Git 操作立即终止。
理解这一点非常关键。很多人遇到这个报错,第一反应是“网络断了”,然后跑去重启路由器、换 WiFi,折腾半天没效果。如果你能确认其他网络访问都正常,那么问题基本就锁定在“目标地址或端口”这一层。另外这个报错偶尔还会伴随一个变体:“stream disconnected before completion: transport error: network error: error”。这个变体本质上是连接建立成功之后、数据传输中途被中断,常见于大文件推送或拉取时,后面第 5 章我会单独展开。
1.2 别把 DNS 问题、超时问题和 Connection refused 混为一谈
我接触过不少新手,看到 Connection refused 就跑去检查域名解析,折腾半天毫无收获。给你几个典型报错关键词的对比,自己对照一下:
| 报错关键词 | 含义 | 典型案例 |
|---|---|---|
| Could not resolve host | 域名解析失败 | 地址写错、DNS 服务器挂了 |
| Connection refused | 端口连接被拒 | 服务未启动、端口不对、防火墙拦截 |
| Operation timed out | 连接超时 | 网络不通、目标主机无响应 |
| Permission denied (publickey) | 认证失败 | 密钥不对、账号不对 |
从上表能看出,Connection refused 跟“主机能不能到达”没关系,它指向的是“目标端口上的服务是不是活着”。所以排查的第一步不是 ping IP,而是确认目标端口是否有服务在监听。这里有个最常见的场景:连接自己搭的内网 Git 服务(比如 Gitea、GitLab 自建实例),服务器明明开着机,但仓库服务所在的进程或 Docker 容器没起来,或者重启后没有自动启动。这时候你从任何客户端访问,都会立刻弹 Connection refused。
排查 Connection refused 的核心思路就一句话:先确认端口通不通,再确认认证对不对,最后才回头看图形工具配置。按这个顺序来,基本不会走弯路。
2. 环境层排查:目标仓库服务到底起了没
2.1 用最朴素的方法验证端口连通性
在 TortoiseGit 里点来点去之前,我强烈建议先打开 Windows 的命令行工具(PowerShell 或 cmd 都行),用几条基础命令验证网络层。假设你的远程地址是git@gitlab.example.com:2222/group/repo.git,那么你真正要连接的主机和端口就是gitlab.example.com的2222端口。
Windows 10/11 自带的 telnet 客户端默认没安装,可以先不用它。直接用 PowerShell 的Test-NetConnection,这个命令是排查端口最方便的工具:
Test-NetConnection -ComputerName gitlab.example.com -Port 2222如果返回结果里TcpTestSucceeded为True,说明网络层是通的,问题在 Git 认证或 TortoiseGit 配置;如果为False,说明端口不通,需要进一步确认服务状态和防火墙规则。老运维可能更习惯用 netcat,但 Windows 原生没有这个命令。装了 Git for Windows 的话,可以进 Git Bash 里跑:
nc -zv gitlab.example.com 2222命令输出显示succeeded就是通的。这一步相当于给整栋楼打电话问“你家有人吗”,先确认有人接听,后续排查才有意义。
2.2 服务器端服务未启动时怎么自查
如果你有目标服务器的访问权限,登录上去看几项基本就清楚了。以最常见的 Linux 服务器为例,Git 服务可能是 OpenSSH(裸仓库或自建 Git 服务常用)、Gitea、GitLab 或 Gogs 这类平台。分别查看:
# 查看 SSH 是否在运行 systemctl status sshd # 查看端口监听情况 netstat -tlnp | grep -E ':(22|2222|80|443)\s'重点看监听地址。0.0.0.0表示对所有网卡开放,127.0.0.1表示只允许本机访问。我见过有人装了 GitLab,默认监听 127.0.0.1,结果从局域网其他电脑连,怎么连都是 Connection refused,翻配置才发现监听地址绑错了。这种问题属于低级但极其常见,遇到“端口测试不通、服务也显示在跑”的情况,优先检查监听地址。
如果是 Docker 方式部署的 Gitea 或 GitLab,还要额外检查容器状态和端口映射:
docker ps -a docker logs <容器名>容器异常退出、端口映射丢失、宿主机防火墙规则变化,这三种情况都会表现为 Connection refused。Docker 容器退出通常有日志可查,docker ps -a里 STATUS 那一列会显示Exited (0)或Exited (1),配合docker logs基本能定位到原因,比如数据库连不上、磁盘空间不足、配置文件语法错误等。
2.3 防火墙和端口占用:两个容易被忽略的方向
服务器端防火墙是人的第一反应,但客户端 Windows 防火墙也常常出来添乱。TortoiseGit 的 SSH 客户端是一个独立的 .exe 进程,第一次运行时 Windows Defender 可能弹窗拦截,如果当时你手滑点掉了“允许”,之后每次连接都会被本地防火墙掐掉,表现就是 Connection refused 或者超时。
检查 Windows 防火墙规则,可以用 PowerShell:
# 查看防火墙是否放行了 TortoiseGit 和 ssh 相关规则 Get-NetFirewallRule | Where-Object { $_.DisplayName -match 'Tortoise|OpenSSH|ssh' } | Select DisplayName, Enabled, Action如果是企业环境,还要考虑安全软件(杀毒、EDR)默认拦截了 SSH 出站连接。这类问题很隐蔽,光看报错根本定位不到,只能靠经验排查。我自己就遇到过公司终端管理软件静默拦截 Git 进程的情况,换了台电脑就通了,回来查日志才发现问题。
还有一个方向容易被忽略:本机端口被程序占用。不过这个场景相对少见,通常还是远程服务端的问题居多。不管是哪种情况,验证思路是一样的——先用Test-NetConnection确认哪一层有问题,再逐层向下挖。
3. TortoiseGit 客户端配置检查:经常在这里出问题
3.1 三个 SSH 客户端怎么选
TortoiseGit 最让人困惑的地方,就是安装过程中会让你选择使用哪种 SSH 客户端。这个选择的后果可能当时看不出来,半年后才在一次连接失败中暴露。TortoiseGit 支持三种 SSH 模式:
| SSH 模式 | 依赖组件 | 适用场景 |
|---|---|---|
| TortoiseGit 自带(基于 PuTTY) | 需要 Pageant 加载 .ppk 密钥 | 纯图形界面操作,不想碰命令行 |
| OpenSSH(Windows 系统自带) | 需要 Windows ssh-agent 服务运行 | 平时也用命令行 git,习惯 OpenSSH 密钥 |
| 自定义 SSH 客户端 | 任意指定的 ssh.exe | 有特殊管理需求,或公司有统一认证要求 |
如果你选的是 TortoiseGit 自带模式,密钥文件必须是 PuTTY 格式(.ppk),而且要提前在 Pageant 里加载。很多人把 GitHub 上生成的 OpenSSH 私钥(id_rsa)直接拿过来用,连的时候就会报格式错误或者认证失败。这跟 Connection refused 是两个层面的问题,但如果密钥加载失败,TortoiseGit 有时候不会立刻报认证错误,而是先在连接阶段各种中断,最后弹出来的信息五花八门,很容易被误导成网络问题。
我自己就摔过这个跟头。第一次用 TortoiseGit 连 GitHub,公钥明明在平台上配好了,私钥也在本地,但就是连不上。折腾了半天,最后发现 TortoiseGit 默认用 PuTTY 模式去读我的 OpenSSH 密钥,格式不匹配。换成 OpenSSH 模式后,一次就通了。这个经验我后来给不少人讲过,几乎每个人都中过招。
3.2 Pageant 与 ssh-agent:没人认领的密钥
选 PuTTY 模式后,Pageant 必须处于运行状态,并且密钥已经加载进去。Pageant 是 PuTTY 体系的密钥代理,相当于一个钥匙扣,你把 .ppk 私钥放进去,TortoiseGit 连接时才会拿它做认证。如果 Pageant 没启动,连接就会失败。麻烦的是,这种失败的表现往往不是“authentication failed”,而是连接阶段就中断或者直接报网络错误,极容易被误判成网络问题。
Windows 10/11 自带的 OpenSSH 也有对应的密钥代理服务,叫 ssh-agent,但它默认是“手动”启动模式。如果你在 TortoiseGit 里选了 OpenSSH 模式,而 ssh-agent 服务没起来,系统根本不知道去哪找你的私钥,连接同样会失败。
# 查看 ssh-agent 服务状态 Get-Service ssh-agent # 如果没运行,先启动它 Set-Service -Name ssh-agent -StartupType Manual Start-Service ssh-agent这里我要给 Windows 用户一个非常实用的建议:只用一种密钥管理方式。如果你平时用 Git Bash 和命令行比较多,就统一用 OpenSSH 模式加系统 ssh-agent;如果你更习惯图形界面、不想碰命令,就用 TortoiseGit 自带模式加 Pageant。两套混着用,或者一个仓库用这套、另一个用那套,早晚踩坑。排查连接被拒的时候,先确认密钥代理在跑、密钥已经加载,再去分析网络,能省很多时间。
3.3 remote URL 与分支操作的隐藏影响
排查网络问题的同时,别忽略仓库的远程地址配置。打开命令行,进入仓库目录,输入:
git remote -v仔细看一眼输出的地址。常见问题有三种:clone 时用了 HTTP 地址,后来服务端只允许 SSH 接入;端口号写错,比如服务端改成了 2222 但 URL 里还是 22;或者仓库迁移到了新域名,旧地址对应的服务器已经下线。这些情况都会导致 Connection refused。很多人换了 Git 服务商却不更新 remote URL,在 TortoiseGit 里反复重试,当然一直失败。
另外有个细节容易被忽视:分支切换和 cherry-pick 这类操作本身不会触发 Connection refused,但如果你在 TortoiseGit 里做这些操作时报网络错误,大概率是本地远程引用已经过期——远程分支被删了、别人强行推送覆盖了、或者仓库迁移了。遇到这种情况,先右键仓库,TortoiseGit → Fetch,把远程引用刷新一遍,再尝试切换分支或合并代码,报错往往就消失了。
我见过一个真实案例,同事本地有十几个分叉分支,远程早被清理了一轮,他在 TortoiseGit 里点“切换分支”,每次都弹网络错误,气得差点重装系统。我过去一看,网络完全正常,就是远端引用同步的问题。Fetch 一次解决。所以说,Connection refused 不一定真是“网络”问题,名字只是表象。
4. 分场景排查:本地内网、公网托管与公司环境
4.1 场景一:连接自建内网 Git 服务
自己在家或公司内网搭 Git 服务,最常见组合是一台服务器(或 NAS)加 Gitea/GitLab,再配上 SSH 端口转发。这种场景下 Connection refused 的高发原因,按概率排序是:
- 服务未启动(最常见)
- 端口映射错误(路由器或 NAS 的端口没转出去)
- 监听地址绑定到了 127.0.0.1
- 服务器重启后 Docker 容器没有自动重启
如果是群晖、威联通这类 NAS,要额外确认 Docker 容器的重启策略是否为always。很多 NAS 服务商默认把容器设为不自动重启,断电一次,所有 Git 服务全部失联,页面访问也报错。设置方法不复杂,在容器详情里找到 Restart policy,改成 Unless stopped 或 Always 就行。
还有一个对内网环境特别实用的检查点:如果服务器走的是路由器端口映射,客户端从外网访问,还要确认路由器把外网端口映射到了正确内网机器上。路由器管理页面里的“端口转发”规则,经常因为 DHCP 动态分配导致内网 IP 变化,规则指向了一台已经换掉的机器,这时候从外网连就是 Connection refused,但在内网连一切正常。遇到这种情况,去路由器里给服务器绑一个固定 IP(DHCP 静态分配),再重新配置端口转发即可。
4.2 场景二:连接公网托管平台
连接 GitHub、Gitee 这类公网平台时,端口通常是标准的(SSH 为 22,HTTPS 为 443)。遇到 Connection refused,原因大概率是网络环境问题,而不是平台本身故障。
排查姿势很固定,直接用命令行验证:
ssh -T -p 22 git@github.com如果这条命令能通,平台一般会返回一句Hi xxx! You've successfully authenticated,说明链路没问题,问题集中在 TortoiseGit 自身配置;如果命令刚跑完就显示连接被拒绝,那就是网络层被拦了。
还有个对比测试方法:把 remote URL 从 SSH 临时改成 HTTPS,在 TortoiseGit 里重新 clone 一次。HTTPS 走 443 端口,一般不会被网络策略限制。如果能正常 clone,基本可以断定是 22 端口出站被限制,这时候最省事的办法就是改用 HTTPS 协议操作仓库——注意如果仓库开启了双因子认证,HTTPS 方式通常需要配置 Personal Access Token,用户名密码是登不进去的。如果不方便用 HTTPS,也可以去托管平台官方帮助文档里查一下是否支持替代端口方案,这个不同平台差异较大,以官方文档为准。
4.3 场景三:公司内网环境与代理设置
公司内网通常有统一的网络出口,如果你的 Git 远程地址是内网域名,但 Windows 上配置了全局 HTTP 代理(比如为了访问某些内部系统),Git 的 HTTP 相关操作就会尝试走代理连接。如果代理服务器无法转发 SSH 流量,连接就会报 Connection refused。这种情况的外在表现和纯网络不通几乎一样,但排查思路完全不同。
先看 Git 的全局配置:
git config --global --list如果看到http.proxy、https.proxy这类条目,就要意识到 Git 的 HTTP 操作会走代理。SSH 操作走的是独立通道,理论上不受 http.proxy 影响,但如果你用的远程地址本身就是 HTTPS 形式,代理配置就会掺和进来。建议排查时让 Git 处于一个“干净”状态,把全局代理临时移除再测试:
git config --global --unset http.proxy git config --global --unset https.proxy在公司环境里,还要注意是否有统一部署的安全终端软件拦截了出站连接。这类软件通常不会在 Git 任何日志里留痕迹,但你可以通过对比测试判断:拿一台没装安全软件的电脑(比如个人笔记本)连同一个远程地址,如果通了,问题就在安全策略上。
5. 常见问题速查表与实操心得
5.1 报错速查表
| 报错场景 | 最快排查命令 | 最可能的解决方法 |
|---|---|---|
| clone 内网仓库被拒 | Test-NetConnection <主机> -Port <端口> | 启动服务端 Git 服务或 Docker 容器 |
| 服务器重启后连不上 | systemctl status sshd(服务器) | 设置服务自启、容器重启策略改 Always |
| 密钥配好仍报 connection 类错误 | 检查 Pageant / ssh-agent 状态 | 加载正确的 .ppk 或启动 ssh-agent 服务 |
| 公网平台连接被拒 | ssh -T -p 22 git@github.com | 确认出口网络正常,必要时改用 HTTPS |
| 公司内网连接被拒 | git config --global --list | 检查代理配置和 remote URL |
| 传输中途断开 | git push大文件时报错 | 分批提交、检查服务端超时参数 |
关于“stream disconnected before completion: transport error: network error”这个变体,我多说几句。它通常出现在数据量较大的 push/pull 过程中,最典型的就是往仓库推一个超过 100MB 的文件,或者从远端拉取大量历史记录时。常见原因有三个:网络代理切断了空闲连接、服务器端超时配置过短、本地网络不稳定。解决思路是:一次少传一点,大文件单独提交;调整客户端的连接超时设置;如果服务端是你自己搭的,检查 sshd 配置里的ClientAliveInterval和ClientAliveCountMax参数,适当调大间隔,避免长时间传输被误判为死连接。
5.2 我的几条实操心得
排查这类问题最常见的坑,就是太早“猜答案”而跳过验证环节。我现在的固定套路是:先验证端口,再验证密钥,最后才看 TortoiseGit 图形配置。前两个问题占了八成以上比例,等你把这两步都查过,大部分问题已经浮出水面。
心得一:命令行 git 一定要装好,并且熟练用几条关键命令。git remote -v、ssh -T、Test-NetConnection,这三条能解决 90% 的定位问题。这不是让你抛弃 TortoiseGit,而是让你手里多一把排查问题的尺子。图形界面适合日常操作,命令行适合关键时刻定位问题,两条腿走路才稳。
心得二:连接配置变更后,一定要在 TortoiseGit 里清理旧状态。有些版本对远程地址和凭据有缓存,修改 remote 后旧连接信息仍然生效。具体操作是:右键仓库 → TortoiseGit → Settings → Git → Remote,确认修改后的 URL 正确,然后重新 Fetch。如果还不行,退出 TortoiseGit 进程重新打开,或者在 Windows 凭据管理器里删掉旧的 Git 凭据再试。这个操作看起来简单,但能解决很多“明明配置没问题却一直报错”的怪问题。
心得三:当遇到“端口测试是通的,但连接还是被拒绝”的情况,别急着重装 TortoiseGit,重点去看认证层。Linux 服务器上 sshd 的日志通常写在/var/log/auth.log或/var/log/secure,Windows 服务器上 OpenSSH 也有对应的事件日志。这些日志会直接告诉你认证失败的具体原因,比如密钥不被接受、账号不存在、密码过期等。定位网络问题靠命令,定位认证问题靠日志,两者配合才能快速收敛。
最后再分享一个小技巧:在 TortoiseGit 设置里把日志级别调成“详细”,然后复现一次报错,日志文件会记录完整的执行命令和服务端响应。这个日志文件通常位于仓库目录下的TortoiseGit文件夹中,或者通过右键菜单 → TortoiseGit → Show Log 查看。很多时候,报错弹窗只给了你一个笼统的“Connection refused”,但日志里藏着的细节——比如具体是哪一步失败、服务端返回了什么——才是解决问题的钥匙。别嫌日志长,排查网络问题的时候,它比任何搜索引擎都管用。