# WebTCP
**Repository Path**: shtml/web-tcp
## Basic Information
- **Project Name**: WebTCP
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-08-25
- **Last Updated**: 2026-08-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# WebTCP Client / Server
一个面向 PLC、DTU、IoT 设备与 Socket 服务的网页版 TCP Client / Server 网络调试工具。浏览器负责交互与编码,Go 后端建立或监听真实 TCP Socket,并通过 WebSocket 双向转发任意二进制数据。
> 安全提示:本服务既能代理 TCP 连接,也能在后端主机上打开 TCP 监听端口。若部署到公网,必须在反向代理或网关层增加身份认证、访问控制、审计与网络隔离,并使用防火墙严格限制端口范围。
## 架构与数据流
```text
Vue 3 / Element Plus / Pinia
│ HTTP + WebSocket (JSON / Base64)
▼
Gin / Gorilla WebSocket
│ 每个浏览器一个 WSClient
├── Client: SessionManager ── net.Conn ── PLC / DTU
└── Server: ListenerManager ── net.Listener
└── accepted session ── TCP Client
SQLite: 连接配置、命令模板、应用设置
LocalStorage: 普通发送历史
内存: TX / RX 实时日志(不写数据库)
```
- TCP 层只处理 `[]byte`,不理解 Text 或 HEX。
- 前端将 Text / HEX 编码成 `Uint8Array`,再以 Base64 放入 WebSocket JSON。
- 一个 WebSocket 可管理多个 TCP Session 和 Listener;Server 界面可在多个接入客户端间选择当前回发目标。
- Gorilla WebSocket 由单独 `writeLoop` 串行写入;TCP 同样通过互斥锁串行写入。
- WebSocket 断开会关闭它创建的所有 TCP Session、Listener 和已接入连接,并释放相关 goroutine。
- TCP 是字节流,界面按每次 `conn.Read()` 返回的数据块原样显示,不擅自拆包。
## 已实现功能
- IPv4、IPv6、域名 TCP Client;连接、断开和连接状态详情
- TCP Server 监听 IPv4 / IPv6 地址;启动、停止、客户端接入与断开状态
- 多客户端收包来源标识,可选择当前客户端回发 Text / HEX 数据
- Text(UTF-8 / ASCII)与 HEX 发送,CR / LF / CRLF,`Ctrl + Enter`
- Base64 二进制传输;HEX、Text、HEX + Text 接收显示
- 暂停显示、自动滚动、清空、5000 条上限、TXT 导出
- 循环发送(最小 100 ms),断线自动停止
- TX/RX 包数、字节数、实时速率与连接时长
- LocalStorage 历史去重、计数、收藏、载入、立即发送、单删和清空
- SQLite 连接配置、命令模板与设置 REST API,自动建库与 migration
- WebSocket Ping/Pong、资源限制、Context 生命周期和优雅退出
- 解析 DNS 后再校验 IP,默认禁止 loopback、link-local、组播与保留地址
- Docker、Go Embed 单二进制与 Nginx + Go 两种生产部署方式
## 目录
```text
frontend/ Vue 3 + TypeScript + Vite
backend/
cmd/server/ 入口与优雅退出
internal/api/ REST、路由与中间件
internal/database/ SQLite 与 migration
internal/security/ 目标地址 / 端口策略
internal/tcp/ TCPSession、SessionManager 与 ListenerManager
internal/websocket/ 协议、客户端单写循环与 Hub
web/dist/ Go Embed 前端产物
deploy/nginx.conf
Dockerfile
docker-compose.yml
```
## 本地开发
要求 Node.js 20.19+(推荐 22)、pnpm 或 npm,以及 Go 1.24+。
后端:
```bash
cd backend
cp .env.example .env
go mod tidy
go run ./cmd/server
```
前端(另一个终端):
```bash
cd frontend
npm install
npm run dev
```
打开 `http://localhost:5173`。Vite 已将 `/api` 与 `/ws` 代理到 `127.0.0.1:8080`。
## 配置
复制 `.env.example` 后按环境修改:
| 变量 | 默认值 | 说明 |
| --- | ---: | --- |
| `APP_HOST` / `APP_PORT` | `0.0.0.0` / `8080` | HTTP 监听地址 |
| `SQLITE_PATH` | `./data/tcp-client.db` | SQLite 文件 |
| `TCP_CONNECT_TIMEOUT` | `5000` | 默认连接超时,毫秒 |
| `TCP_IDLE_TIMEOUT` | `1800000` | TCP 空闲超时,毫秒 |
| `MAX_TCP_SESSIONS` | `10` | 单浏览器 Session 上限 |
| `MAX_TCP_LISTENERS` | `5` | 单浏览器 TCP Listener 上限 |
| `MAX_SEND_BYTES` | `1048576` | 单次发送上限 |
| `MAX_WS_MESSAGE_BYTES` | `2097152` | WebSocket 消息上限 |
| `ALLOW_PRIVATE_IP` | `true` | 是否允许 RFC1918 私网地址 |
| `BLOCKED_PORTS` | 空 | 逗号分隔端口黑名单 |
| `ALLOWED_ORIGINS` | 本地 Vite | 开发环境 CORS / WS Origin 白名单 |
不论 `ALLOW_PRIVATE_IP` 如何设置,loopback、`0.0.0.0/8`、link-local、组播和保留地址始终禁止。公网部署建议设置类似:
```env
ALLOW_PRIVATE_IP=false
BLOCKED_PORTS=22,25,3306,5432,6379,2375,2376,9200,27017
ALLOWED_ORIGINS=https://tcp.example.com
```
## 测试与构建
```bash
cd frontend
npm test
npm run build
cd ../backend
go test ./...
go vet ./...
go build -o tcp-client ./cmd/server
```
## 生产方式 A:Nginx + Go
```bash
cd frontend
npm ci
npm run build
sudo cp -R dist/* /usr/share/nginx/webtcp/
cd ../backend
CGO_ENABLED=0 go build -trimpath -o tcp-client ./cmd/server
./tcp-client
```
将 `deploy/nginx.conf` 放入 Nginx 配置目录并重载。`/api` 与 `/ws` 会代理到 Go `:8080`。
## 生产方式 B:Go Embed 单二进制
```bash
make embed
cd backend
./tcp-client
```
`make embed` 会构建 Vue、复制产物到 `backend/web/dist` 并生成不依赖 CGO 的单一 Go 二进制。运行时只需二进制和可写的 `data/` 目录。
## Docker
```bash
docker compose up -d --build
```
访问 `http://server:8080`。SQLite 位于命名卷 `webtcp-data` 的 `/app/data/tcp-client.db`,容器重建不会丢失。
注意:容器需要能够路由到被调试设备。跨 VLAN、Docker Desktop 或防火墙环境中,请先确认容器到 PLC / DTU 地址与端口的网络连通性。
TCP Server 的监听发生在容器内。若需要让容器外的设备连接,例如在界面监听 `9000`,还需在 `docker-compose.yml` 的 `ports` 中发布同一端口后重建容器:
```yaml
ports:
- "8080:8080"
- "9000:9000"
```
非容器部署不需要此映射,但仍需在主机防火墙中允许相应端口。监听 `0.0.0.0` 表示所有 IPv4 网卡,监听 `::` 表示 IPv6。监听器归属当前浏览器 WebSocket,页面关闭或 WebSocket 断开时会自动停止。
## REST 与 WebSocket
REST:
```text
GET/POST /api/connection-profiles
PUT/DELETE /api/connection-profiles/:id
GET/POST /api/command-templates
PUT/DELETE /api/command-templates/:id
GET/PUT /api/settings
GET /healthz
```
WebSocket:`GET /ws`。所有业务消息包含 `type`、`requestId`、`sessionId` 与 `payload`;TCP 字节统一使用 Base64。
- Client:`tcp.connect` / `tcp.connected`、`tcp.disconnect`、`tcp.send`、`tcp.receive`、`tcp.closed`
- Server:`tcp.listen` / `tcp.listening`、`tcp.unlisten`、`tcp.accepted`、`tcp.accept_error`、`tcp.server_closed`
- Server 接入的客户端也使用普通 Session ID,因此回发仍使用 `tcp.send`;`tcp.receive` 会额外携带 Listener ID 与远端地址。