当前位置: 首页 > 知识库问答 >
问题:

网TCPConn允许FIN数据包后写入

王英奕
2023-03-14

我正试图为一些服务器端代码编写单元测试,但在确定关机测试用例时遇到了问题。环回TCP连接似乎没有正确处理干净的关机。我在一个示例应用程序中对此进行了重新编程,该应用程序在lockstep中执行以下操作:

  1. 创建客户机

步骤4成功无误。我尝试过使用json。编码器和对TCPConn的裸调用。写我和WireShark检查了交通状况。服务器发送了一个FIN数据包,但客户端从未发送(即使是1s睡眠),服务器甚至发送了一个RST数据包来响应(4),客户端conn.Write仍然返回nil作为其错误。

这似乎完全疯了。我是不是漏了什么?目前运行Go v1.2.1/Darwin

编辑:强制性重现

package main

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

var (
  loopback = make(chan string)
  shouldClose = make(chan struct{})
  didClose = make(chan struct{})
)

func serve(listener *net.TCPListener) {
  conn, err := listener.Accept()
  if err != nil {
    panic(err)
  }

  s := bufio.NewScanner(conn)
  if !s.Scan() {
    panic(fmt.Sprint("Failed to scan for line: ", s.Err()))
  }

  loopback <- s.Text() + "\n"

  <-shouldClose
  conn.Close()
  close(didClose)

  if s.Scan() {
    panic("Expected error reading from a socket closed on this side")
  }
}

func main() {
  listener, err := net.ListenTCP("tcp", &net.TCPAddr{})
  if err != nil {
    panic(err)
  }
  go serve(listener)

  conn, err := net.Dial("tcp", listener.Addr().String())
  if err != nil {
    panic(fmt.Sprint("Dialer got error ", err))
  }

  oracle := "Mic check\n"
  if _, err = conn.Write([]byte(oracle)); err != nil {
    panic(fmt.Sprint("Dialer failed to write oracle: ", err))
  }

  test := <-loopback
  if test != oracle {
    panic("Server did not receive the value sent by the client")
  }

  close(shouldClose)
  <-didClose

  // For giggles, I can also add a <-time.After(500 * time.Millisecond)
  if _, err = conn.Write([]byte("This should fail after active disconnect")); err == nil {
    panic("Sender 'successfully' wrote to a closed socket")
  }
}

共有1个答案

龙俭
2023-03-14

这就是TCP连接主动关闭的工作方式。当客户端检测到服务器已关闭时,它将关闭其一半的连接。

在你的例子中,不是关闭客户端,而是发送更多的数据。这将导致服务器发送RST数据包以强制关闭连接,因为收到的消息无效。

如果您仍然不确定,这里有一个与之相当的python客户端服务器,它显示相同的行为。(我发现使用python很有帮助,因为它完全遵循底层BSD套接字API,而不使用C)

服务器:

import socket, time

server = socket.socket()
server.bind(("127.0.0.1", 9999))
server.listen(1)
sock, addr = server.accept()
msg = sock.recv(1024)
print msg
print "closing"
sock.close()
time.sleep(3)
print "done"

客户:

import socket, time

sock = socket.socket()
sock.connect(("127.0.0.1", 9999))
sock.send("test\n")
time.sleep(1)
print "sending again!"
sock.send("no error here")
time.sleep(1)
print "sending one last time"
sock.send("broken pipe this time")

要正确检测连接上的远程关闭,应执行Read(),并查找io。EOF返回错误。

// we technically need to try and read at least one byte, 
// or we will get an EOF even if the connection isn't closed.
buff := make([]byte, 1)
    if _, err := conn.Read(buff); err != io.EOF {
    panic("connection not closed")
}
 类似资料:
  • 问题内容: 我希望我的应用程序将应用程序数据库归档到SD卡。在我的代码中,我检查目录是否存在,如果不存在,则抛出。在此特定情况下,我尝试将db文件复制到SD卡上的根目录,但是会抛出。如何更改文件夹/文件的权限以能够写入? 问题答案: 没错,SD卡目录位于其中,但您不应该对其进行硬编码。而是调用以获取目录: 如果尚未执行此操作,则需要在清单中添加以下行,以授予您的应用正确的写入SD卡的权限:

  • 我正在使用FireBase实时数据库来设置一个简单的CRUD应用程序。 我正在设置规则,以便只有登录的用户才能写入请求部分。我一定是误会了奥特。uid。 在请求中,我设置了,但我无法写入数据。 我错过了什么?

  • 我一直在捕捉非数字时遇到问题。 我试过了,但抓不住。如果我让它捕获非数字,但不让用户再次尝试输入。。。它完全停止了我的代码。 这是我的密码:

  • 如果我试图写入脚本的目录或任何其他目录,则错误是相同的。权限似乎是对的。所有东西都是755,根所有者。我尝试将“my_custom_dir”所有者更改为www-data(apache用户),但没有任何区别。 它“死”在第一线。日志上的错误是“在/usr/lib/cgi-bin/script.cgi拒绝权限”

  • 我有一个,我希望将用户的输入限制为纯数字或小数最多2位的数字。 基本上,我要求的是一个价格输入。 我想避免做正则表达式。有办法做到吗?

  • 我遇到了MySQL的问题。我收到错误。关于StackOverflow,他们说MySQL配置中的很可能太低。 我尝试在中更改它,但该文件是不可编辑的。不能在PA上使用。 我正在使用Sqlalchemy处理与MySQL服务器的交互。 我能做什么?