惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
U
Unit 42
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
F
Full Disclosure
月光博客
月光博客
Engineering at Meta
Engineering at Meta
博客园_首页
The Register - Security
The Register - Security
G
Google Developers Blog
The Cloudflare Blog
博客园 - Franky
K
Kaspersky official blog
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cisco Blogs
Hugging Face - Blog
Hugging Face - Blog
C
Check Point Blog
NISL@THU
NISL@THU
AI
AI
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
Project Zero
Project Zero
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
量子位
Vercel News
Vercel News
T
Tor Project blog
P
Privacy International News Feed
D
Docker
I
Intezer
L
LangChain Blog
P
Proofpoint News Feed
Security Latest
Security Latest
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
博客园 - 聂微东
AWS News Blog
AWS News Blog
Martin Fowler
Martin Fowler
P
Privacy & Cybersecurity Law Blog
V
V2EX
Last Week in AI
Last Week in AI
C
Cybersecurity and Infrastructure Security Agency CISA
The Hacker News
The Hacker News
T
Tenable Blog
Blog — PlanetScale
Blog — PlanetScale
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog

博客园 - 汉克书

新工作新气象 记录一些skill的网站 重构代码的思路想法 增加一下git提交记录的关键词 ai生成一段学习golang的select、context、go、channel的代码 水一篇博文 文件服务器对于devops的用处 git信息提交错误,进行修改 还是没啥东西写的,写点DevOps的想法 突然想了一些“思路”问题。提出的一些想法 就是想吐槽一下信创 这个月好像没啥可以写的,rest client mac和linux终端显示分支 继续一个日常 devops 这个月又没什么东西可以写的,就写个harbor的arm版本获取地方吧 写一个日常吧 docker 在x86运行arm容器 ansible的最佳实践 临时用的
内网服务器如何跟服务端通信
汉克书 · 2026-01-13 · via 博客园 - 汉克书
  1. 内网服务器是一个只能通信外网,不对外开放任何端口
  2. 服务端的端口号是部署在外网的
  3. 服务端如何告诉内网服务器消息

简单来说,就是服务端使用一个长连接的socket,然后内网服务器连接这个IP+端口号进行长连接。

服务端发现有人来连接,就可以通过这个连接进行消息发送

以下是让AI写的一个tcp+http服务的代码

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "net/http"
    "sync"
    "time"
)

// Client 代表一个连接的客户端
type Client struct {
    ID       string
    Conn     net.Conn
    Outgoing chan []byte // 用于向客户端发送数据
}

// 全局客户端管理器
var (
    clients    = make(map[string]*Client)
    clientsMu  sync.RWMutex
    nextID     = 1
    nextIDMu   sync.Mutex
)

// 生成唯一客户端 ID
func generateClientID() string {
    nextIDMu.Lock()
    id := fmt.Sprintf("client-%d", nextID)
    nextID++
    nextIDMu.Unlock()
    return id
}

// TCP 服务端:处理客户端连接
func tcpServer() {
    listener, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatal("TCP server failed:", err)
    }
    defer listener.Close()
    log.Println("TCP server listening on :8081")

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Println("Accept error:", err)
            continue
        }

        clientID := generateClientID()
        client := &Client{
            ID:       clientID,
            Conn:     conn,
            Outgoing: make(chan []byte, 100), // 带缓冲,避免阻塞
        }

        // 注册客户端
        clientsMu.Lock()
        clients[clientID] = client
        clientsMu.Unlock()

        log.Printf("New client connected: %s\n", clientID)

        // 启动读写 goroutine
        go handleClientRead(client)
        go handleClientWrite(client)
    }
}

// 从客户端读取数据(可选:用于心跳或日志)
func handleClientRead(client *Client) {
    reader := bufio.NewReader(client.Conn)
    for {
        msg, err := reader.ReadString('\n')
        if err != nil {
            log.Printf("Client %s disconnected: %v", client.ID, err)
            cleanupClient(client)
            return
        }
        log.Printf("Received from %s: %s", client.ID, msg)
    }
}

// 向客户端写入数据
func handleClientWrite(client *Client) {
    defer func() {
        client.Conn.Close()
    }()

    for msg := range client.Outgoing {
        _, err := client.Conn.Write(msg)
        if err != nil {
            log.Printf("Write to %s failed: %v", client.ID, err)
            cleanupClient(client)
            return
        }
    }
}

// 清理断开的客户端
func cleanupClient(client *Client) {
    clientsMu.Lock()
    delete(clients, client.ID)
    close(client.Outgoing)
    clientsMu.Unlock()
    log.Printf("Client %s removed", client.ID)
}

// HTTP 服务:提供 Web 页面和 API
func httpServer() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        clientsMu.RLock()
        defer clientsMu.RUnlock()

        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, `
<!DOCTYPE html>
<html>
<head><title>TCP Client Control</title></head>
<body>
<h2>Send Command to Client</h2>
<form method="POST" action="/send">
  <label>Client:
    <select name="client_id" required>
      <option value="">-- Select --</option>
      %s
    </select>
  </label><br><br>
  <label>Command:<br>
    <textarea name="command" rows="4" cols="50" placeholder="Enter command (will append \\n)" required></textarea>
  </label><br><br>
  <button type="submit">Send</button>
</form>
</body>
</html>
`, buildClientOptions())
    })

    http.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        clientID := r.FormValue("client_id")
        command := r.FormValue("command")
        if clientID == "" || command == "" {
            http.Error(w, "Missing client_id or command", http.StatusBadRequest)
            return
        }

        clientsMu.RLock()
        client, exists := clients[clientID]
        clientsMu.RUnlock()

        if !exists {
            http.Error(w, "Client not found", http.StatusNotFound)
            return
        }

        // 发送命令(确保以 \n 结尾,便于客户端读取)
        if command[len(command)-1] != '\n' {
            command += "\n"
        }
        select {
        case client.Outgoing <- []byte(command):
            log.Printf("Sent to %s: %s", clientID, command)
            fmt.Fprintf(w, "✅ Sent to %s: %s", clientID, command)
        default:
            http.Error(w, "Client send buffer full", http.StatusInternalServerError)
        }
    })

    log.Println("HTTP server listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

// 构建客户端下拉选项
func buildClientOptions() string {
    clientsMu.RLock()
    defer clientsMu.RUnlock()

    var options string
    for id := range clients {
        options += fmt.Sprintf("<option value=\"%s\">%s</option>\n", id, id)
    }
    return options
}

func main() {
    go tcpServer()
    httpServer()
}