- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我试图通过检查 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 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,这就是冒泡的原因进入您看到的错误。
回到您的程序,您需要以某种方式处理它。通常你可以想到谁负责发起发送,谁也负责发起终止,因此你的服务器应该假设它可以继续发送直到它关闭连接,或遇到错误.如果您需要更可靠地检测客户端关闭,则需要在协议(protocol)中包含某种客户端响应。这样可以在套接字上调用 recv 并返回 0,这会提醒您连接已关闭。
在 go 中,这将从连接的 Read 方法(或在您的情况下从 Copy 中)返回 EOF 错误。 SetWriteDeadline 不起作用,因为小的写入会通过并被静默丢弃,或者客户端最终会响应 RST,给你一个错误。
关于tcp - golang TCPConn.SetWriteDeadline 似乎没有按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15067286/
我有几个 TCPConn 对象。我想运行 goroutine,它会定期检查我的 TCPConns 是否还活着,如果有人死了,我想关闭这些 TCPConn 并从内存中删除。 如何检查 TCPConn 对
我正在用 Golang 编写一个 TCP 客户端。服务器永远不会发送任何回复或任何数据。 一旦我在给定的超时后无法写入,我希望客户端关闭连接。 因此,通过阅读 Conn 中的 SetWriteDead
在套接字或写入器关闭之前,在没有任何换行符或空字节定界符的情况下调用 TCPConn.Write 似乎不会执行任何操作。 在此示例中,我希望 conn.Write 在写入完成后立即触发服务器上的读取,
我遇到了 gob 协议(protocol)问题(或者可能是一般的网络问题,我的知识薄弱),我不明白为什么下面的代码不能正常工作。它只是一个维护开放 TCP 连接并通过它发送多个 gob 的简单示例。该
我正在使用 Go 1.2 并尝试在我的服务器由于输入中的 fatal error 而关闭的套接字上调用 TCPConn.SetLinger(0)。但是,我不知道该怎么做。 net.Listener.A
我在 Mac 上创建了一个简单的 Go 应用程序,用于在 TCP 连接中写入和读取数据。我用过 GAE Go 版本。后来,我把那个程序移植到 Windows 上,我得到了这个错误: Connectio
我试图通过检查 golang TCPConn.Write 返回的错误来检测发送失败,但它是零。我也尝试使用 TCPConn.SetWriteDeadline没有成功。 事情就是这样发生的: 服务器启动
我正在尝试建立一种在运行 Go 的中央服务器和一组物联网设备(也运行 Go)之间进行通信的方法。 对于每个设备,它通过持久的 TCPConn 连接到中央服务器。这些设备位于路由器后面。中央服务器保存该
我正在尝试为一些服务器端代码编写单元测试,但我无法确定关闭测试用例。环回 TCP 连接似乎没有正确处理干净关闭。我在一个示例应用程序中重现了这一点,该应用程序同步执行以下操作: 创建客户端和服务器连接
是否有一个版本的 ioutil.ReadAll 可以读取到 EOF 或读取 n 字节(以先到者为准)? 出于 DOS 的原因,我不能只从 ioutil.ReadAll 转储中取出前 n 个字节。 最佳
我有一个从 TCPListener.acceptTCP 接受的 TCPConn*。我只想获取字符串或 IP 形式的 IP 地址(无端口信息)。实现该目标的正确方法(即最少的转换/字符串操作)是什么?
我正在 golang 中开发 SIP over TCP 模拟服务。传入的 SIP 消息由 '\r\n\r\n' 序列分隔(我现在不关心 SDP)。我想根据该分隔符提取消息并将其发送到处理 gorout
godocs 说: “EOF 是当没有更多输入可用时 Read 返回的错误。函数应该只返回 EOF 以表示输入的正常结束。如果 EOF 在结构化数据流中意外出现,则相应的错误是 ErrUnexpect
在golang中,你会如何处理TCPConn.Read/Write返回的错误?这个document没有说任何关于读/写错误的信息。 最佳答案 TCPConn 上的 Read 和Write 可能会返回您
http://play.golang.org/p/gZo5RqgY4F 我对 io.Copy 方法有疑问。上面的链接将在 Go 1.5 下的第 44 行阻塞。但会在 1.4.2 中通过。我不知道这个问
我试图从与 redis 客户端(一个正在运行 redis-cli 的终端)的 tcp 连接中读取一些消息。然而,Read net 包的命令要求我提供一个 slice 作为参数。每当我给出一个没有长度的
1.我们能否在同一个 net.UDPConn 或 net.TCPConn 对象上从一个线程调用发送并从另一个线程调用接收? 2.我们能否从同一 net.UDPConn 或 net.TCPConn 对象
我是一名优秀的程序员,十分优秀!