golang TCPConn.SetWriteDeadline似乎无法正常工作

我正在尝试通过检查golang

TCPConn.Write返回的错误来检测发送失败,但它为nil。我也尝试使用TCPConn.SetWriteDeadline,但没有成功。

事情就是这样发生的:

  1. 服务器启动
  2. 客户端连接
  3. 服务器发送一条消息,客户端收到它
  4. 客户端 关闭
  5. 服务器再发送一条消息:没有错误
  6. 服务器发送第三条消息:仅现在出现错误

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

代码如下:

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 connected\n")

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: %v\n", 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: %v\n", 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: %v\n", 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: %v\n", 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 connected\n")

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, 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)

}

回答:

这不是特定于Go的,而是通过底层TCP套接字显示出来的工件。

TCP终止步骤的详细图示在此页面的底部:http

:

//www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm

简单的版本是,当客户端关闭其套接字时,它将发送FIN,并从服务器接收ACK。然后,它等待服务器执行相同的操作。但是,您不是发送FIN,而是发送更多的数据,这些数据将被丢弃,并且客户端套接字现在假定来自您的任何其他数据都是无效的,因此下次发送时会收到RST,这就是气泡进入您看到的错误。

回到您的程序,您需要以某种方式进行处理。一般来说,你能想到的任何人负责发起发送的,还负责启动终止,因此,您的服务器应该假设它可以继续发送,直到

关闭连接,或遇到错误。如果需要更可靠地检测到客户端关闭,则需要在协议中具有某种客户端响应。这样,可以在套接字上调用recv并返回0,这将提醒您关闭的连接。

进行中,这将从连接的Read方法(或您的情况下,在Copy中)返回EOF错误。SetWriteDeadline无法正常工作,因为会进行少量写入,然后将其静默删除,否则客户端最终将以RST响应,从而给您带来错误。

以上是 golang TCPConn.SetWriteDeadline似乎无法正常工作 的全部内容, 来源链接: utcz.com/qa/431721.html

回到顶部