golang 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终止步骤的合适图表位于此页面的底部: 简单的版本是当客户端关闭其套接字时,它发送FIN,并从服务器接收ACK.然后它等待服务器执行相同的操作.虽然不是发送FIN,但是你发送了更多的数据,而这些数据被丢弃了,而客户端套接字现在假定来自你的更多数据无效,所以下次你发送的时候会得到一个RST,这就是冒泡的你看到的错误. 回到你的程序,你需要以某种方式处理它.通常,您可以考虑负责启动发送的任何人,也负责启动终止,因此您的服务器应该假设它可以继续发送,直到它关闭连接或遇到错误.如果您需要更可靠地检测客户端关闭,则需要在协议中进行某种客户端响应.这样可以在套接字上调用recv并返回0,它会提醒您关闭连接. 在go中,这将从连接的Read方法(或在您的情况下从Copy中)返回EOF错误. SetWriteDeadline不起作用,因为一个小的写入将通过并静默删除,或者客户端最终将使用RST响应,从而导致错误. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |