加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

golang TCPConn.SetWriteDeadline似乎没有按预期工作

发布时间:2020-12-16 19:04:03 所属栏目:大数据 来源:网络整理
导读:我试图通过检查golang TCPConn.Write返回的错误来检测发送失败,但它没有.我也试过使用 TCPConn.SetWriteDeadline没有成功. 事情就是这样: 服务器启动 客户连接 服务器发送消息,客户端接收消息 客户关闭 服务器再发送一条消息:没有错误 服务器发送第三条消
我试图通过检查golang TCPConn.Write返回的错误来检测发送失败,但它没有.我也试过使用 TCPConn.SetWriteDeadline没有成功.

事情就是这样:

>服务器启动
>客户连接
>服务器发送消息,客户端接收消息
>客户关闭
>服务器再发送一条消息:没有错误
>服务器发送第三条消息:现在只显示错误

问题:为什么只有第二条消息发送给不存在的客户端会导致错误?案件应如何妥善处理?

代码如下:

package main

import (
    "net"
    "os"
    "bufio"
    "fmt"
    "time"
)

func AcceptConnections(listener net.Listener,console <- chan string) {

    msg := ""

    for {

        conn,err := listener.Accept()

        if err != nil {
            panic(err)
        }

        fmt.Printf("client connectedn")

        for {

            if msg == "" {
                msg = <- console
                fmt.Printf("read from console: %s",msg)
            }

            err = conn.SetWriteDeadline(time.Now().Add(time.Second))

            if err != nil {
                fmt.Printf("SetWriteDeadline failed: %vn",err)
            }

            _,err = conn.Write([]byte(msg))

            if err != nil {
                // expecting an error after sending a message
                // to a non-existing client endpoint
                fmt.Printf("failed sending a message to network: %vn",err)
                break
            } else {
                fmt.Printf("msg sent: %s",msg)
                msg = ""
            }
        }
    }
}

func ReadConsole(network chan <- string) {

    console := bufio.NewReader(os.Stdin)

    for {

        line,err := console.ReadString('n')

        if err != nil {

            panic(err)

        } else {

            network <- line
        }
    }
}

func main() {

    listener,err := net.Listen("tcp","localhost:6666")

    if err != nil {
        panic(err)
    }

    println("listening on " + listener.Addr().String())

    consoleToNetwork := make(chan string)

    go AcceptConnections(listener,consoleToNetwork)

    ReadConsole(consoleToNetwork)
}

服务器控制台如下所示:

listening on 127.0.0.1:6666
client connected
hi there!
read from console: hi there!
msg sent: hi there!
this one should fail
read from console: this one should fail
msg sent: this one should fail
this one actually fails
read from console: this one actually fails
failed sending a message to network: write tcp 127.0.0.1:51194: broken pipe

客户端看起来像这样:

package main

import (
    "net"
    "os"
    "io"
    //"bufio"
    //"fmt"
)

func cp(dst io.Writer,src io.Reader,errc chan<- error) {

    // -reads from src and writes to dst
    // -blocks until EOF
    // -EOF is not an error
    _,err :=  io.Copy(dst,src)

    // push err to the channel when io.Copy returns
    errc <- err
}

func StartCommunication(conn net.Conn) {

    //create a channel for errors
    errc := make(chan error)

    //read connection and print to console
    go cp(os.Stdout,conn,errc)

    //read user input and write to connection
    go cp(conn,os.Stdin,errc)

    //wait until nil or an error arrives
    err := <- errc

    if err != nil {
        println("cp error: ",err.Error())
    }
}

func main() {

    servAddr := "localhost:6666"

    tcpAddr,err := net.ResolveTCPAddr("tcp",servAddr)

    if err != nil {
        println("ResolveTCPAddr failed:",err.Error())
        os.Exit(1)
    }

    conn,err := net.DialTCP("tcp",nil,tcpAddr)

    if err != nil {
        println("net.DialTCP failed:",err.Error())
        os.Exit(1)
    }

    defer conn.Close()

    StartCommunication(conn)

}

编辑:按照JimB的建议,我想出了一个有效的例子.消息不会再丢失,并在新连接中重新发送.我不太确定在不同的go例程之间使用共享变量(connWrap.IsFaulted)有多安全.

package main

import (
    "net"
    "os"
    "bufio"
    "fmt"
)

type Connection struct {
    IsFaulted bool
    Conn net.Conn
}

func StartWritingToNetwork(connWrap * Connection,errChannel chan <- error,msgStack chan string) {

    for {

        msg := <- msgStack

        if connWrap.IsFaulted {

            //put it back for another connection
            msgStack <- msg

            return
        }

        _,err := connWrap.Conn.Write([]byte(msg))

        if err != nil {

            fmt.Printf("failed sending a message to network: %vn",err)

            connWrap.IsFaulted = true

            msgStack <- msg

            errChannel <- err

            return

        } else {

            fmt.Printf("msg sent: %s",msg)
        }
    }
}

func StartReadingFromNetwork(connWrap * Connection,errChannel chan <- error){

    network := bufio.NewReader(connWrap.Conn)

    for (!connWrap.IsFaulted) {

        line,err := network.ReadString('n')

        if err != nil {

            fmt.Printf("failed reading from network: %vn",err)

            connWrap.IsFaulted = true

            errChannel <- err

        } else {

            fmt.Printf("%s",line)
        }
    }
}

func AcceptConnections(listener net.Listener,console chan string) {

    errChannel := make(chan error)

    for {

        conn,err := listener.Accept()

        if err != nil {
            panic(err)
        }

        fmt.Printf("client connectedn")

        connWrap := Connection{false,conn}

        go StartReadingFromNetwork(&connWrap,errChannel)

        go StartWritingToNetwork(&connWrap,errChannel,console)

        //block until an error occurs
        <- errChannel
    }
}

func ReadConsole(network chan <- string) {

    console := bufio.NewReader(os.Stdin)

    for {

        line,consoleToNetwork)

    ReadConsole(consoleToNetwork)
}
这不是Go特定的,并且是底层TCP套接字的工件.

TCP终止步骤的合适图表位于此页面的底部:
http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm

简单的版本是当客户端关闭其套接字时,它发送FIN,并从服务器接收ACK.然后它等待服务器执行相同的操作.虽然不是发送FIN,但是你发送了更多的数据,而这些数据被丢弃了,而客户端套接字现在假定来自你的更多数据无效,所以下次你发送的时候会得到一个RST,这就是冒泡的你看到的错误.

回到你的程序,你需要以某种方式处理它.通常,您可以考虑负责启动发送的任何人,也负责启动终止,因此您的服务器应该假设它可以继续发送,直到它关闭连接或遇到错误.如果您需要更可靠地检测客户端关闭,则需要在协议中进行某种客户端响应.这样可以在套接字上调用recv并返回0,它会提醒您关闭连接.

在go中,这将从连接的Read方法(或在您的情况下从Copy中)返回EOF错误. SetWriteDeadline不起作用,因为一个小的写入将通过并静默删除,或者客户端最终将使用RST响应,从而导致错误.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读