Golang中TCP连接 - 每次发送消息前需要重新拨号的问题

Golang中TCP连接 - 每次发送消息前需要重新拨号的问题 我需要一个能够来回交换消息的客户端和服务器。

目前,客户端进行拨号并发送问候信息。 服务器接受连接并回复3次。

客户端只听到第一个回复,然后似乎没有收到另外两个,除非服务器在发送这些消息之前也向客户端拨号。

TCP 应该是这样工作的吗? 如果不是,为什么客户端只接受一条消息?

终端输出… (服务器) 已听到 2022/03/21 01:43:16 新客户端 正在拨号 正在发送消息 正在发送第二条消息 正在发送第三条消息

(客户端) 接受前 已接受 127.0.0.1:53047 接受后 接受前 2022/03/21 01:43:20 收到: 消息

代码… 服务器

package main

import (
    "log"
    "net"
    "time"
    "fmt"
)

var rc net.Conn

func main() {
    ln, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    rc = conn
    buf := make([]byte, 1024)
    n, err := conn.Read(buf)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("heard")
    log.Println(time.Now())
    fmt.Println("newClient")
    time.Sleep(time.Second * 2)
    fmt.Println("Dialing")
    conn2, err := net.Dial("tcp", "127.0.0.1:8081")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("sending msg")
    conn2.Write([]byte("msg"))
    time.Sleep(time.Second * 2)
    fmt.Println("sending 2nd msg")
    conn2.Write([]byte("msg2"))
    time.Sleep(time.Second * 2)
    fmt.Println("sending 3rd msg")
    conn2.Write([]byte("msg3"))
}

客户端

package main

import (
    "net"
    "log"
)

func main() {
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatal(err)
    }
    for {
        fmt.Println("preAccept")
        conn, err := ln.Accept()
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println("accepted", conn.RemoteAddr())
        fmt.Println("postAccept")
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    buf := make([]byte, 1024)
    n, err := conn.Read(buf)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("rcv:", string(buf[:n]))
}

更多关于Golang中TCP连接 - 每次发送消息前需要重新拨号的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我通过让客户端监听拨号连接来接收消息,而不是使用它自己的监听函数,从而解决了这个问题。

更多关于Golang中TCP连接 - 每次发送消息前需要重新拨号的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


buf := make([]byte, 1024)
n, _ := c.Read(buf)
got := string(buf[:n])
log.Print("rcv: "+got)
    return
}

问题在于 handleMessage 只从每个连接读取单条消息。

你需要类似这样的代码:

func handleMessage(c net.Conn){
	buf := make([]byte, 1024)
	for {
		n, err  := c.Read(buf)
		if err != nil {
			break
		}
		log.Print("rcv: %s ", buf[:n])
	}
	c.Close()
}

但是,当然,TCP/IP 是一种流式协议。 因此接收到的消息边界可能与发送时的边界不同。

你的代码存在几个关键问题导致客户端只收到第一条消息:

  1. 服务器端错误地建立了新连接:服务器在收到客户端连接后,又主动向客户端建立了一个新连接(net.Dial),这创建了完全独立的TCP连接。
  2. 客户端只读取一次:客户端的handleConnection函数只调用一次conn.Read(),读取一次后就退出了。

这是修复后的代码:

服务器端

package main

import (
    "fmt"
    "log"
    "net"
    "time"
)

func main() {
    ln, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }
    defer ln.Close()
    
    for {
        conn, err := ln.Acept()
        if err != nil {
            log.Println("Accept error:", err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    
    // 读取客户端问候
    buf := make([]byte, 1024)
    n, err := conn.Read(buf)
    if err != nil {
        log.Println("Read error:", err)
        return
    }
    
    fmt.Printf("Heard: %s\n", string(buf[:n]))
    log.Println(time.Now())
    fmt.Println("New client connected")
    
    // 使用同一个连接发送回复
    time.Sleep(time.Second * 2)
    fmt.Println("Sending 1st message")
    conn.Write([]byte("Message 1\n"))
    
    time.Sleep(time.Second * 2)
    fmt.Println("Sending 2nd message")
    conn.Write([]byte("Message 2\n"))
    
    time.Sleep(time.Second * 2)
    fmt.Println("Sending 3rd message")
    conn.Write([]byte("Message 3\n"))
}

客户端

package main

import (
    "fmt"
    "log"
    "net"
    "time"
)

func main() {
    // 连接到服务器
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
        log.Fatal("Dial error:", err)
    }
    defer conn.Close()
    
    // 发送问候
    fmt.Println("Sending greeting to server")
    conn.Write([]byte("Hello Server\n"))
    
    // 持续读取服务器回复
    buf := make([]byte, 1024)
    for i := 0; i < 3; i++ {
        n, err := conn.Read(buf)
        if err != nil {
            log.Println("Read error:", err)
            return
        }
        log.Printf("Received: %s", string(buf[:n]))
    }
    
    // 或者使用无限循环读取所有消息
    // for {
    //     n, err := conn.Read(buf)
    //     if err != nil {
    //         log.Println("Connection closed:", err)
    //         return
    //     }
    //     log.Printf("Received: %s", string(buf[:n]))
    // }
}

关键修改说明

  1. 服务器端

    • 移除了net.Dial调用,使用ln.Accept()接受的同一个连接发送所有消息
    • 直接使用conn.Write()在原始连接上发送数据
  2. 客户端

    • 改为主动连接到服务器(net.Dial
    • 使用循环读取多次消息,而不是只读取一次
    • 移除了监听端口的代码,因为现在它是TCP客户端

TCP连接的工作方式

  • TCP连接是全双工的,一旦建立,双方都可以通过同一个连接发送和接收数据
  • 不需要为每条消息重新建立连接
  • 连接保持打开状态直到显式关闭或发生错误

这样修改后,客户端将能正确接收所有三条消息,因为它们在同一个TCP连接上传输。

回到顶部