net.DialTCP生产“产品”;“拒绝连接”;Linux上出现错误,但Windows上没有 代码

net.DialTCP生产“产品”;“拒绝连接”;Linux上出现错误,但Windows上没有 代码,linux,windows,go,tcp,Linux,Windows,Go,Tcp,复制需要两个应用程序运行并通过TCP相互连接。因此,我制作了一个小型回购协议,其中还包括powershell构建脚本 然而,为了避免额外的点击,这里是clientA.go的代码 package main import ( "fmt" "net" "time" ) func main() { clientA, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf(":%v", "2222")) if err != n

复制需要两个应用程序运行并通过TCP相互连接。因此,我制作了一个小型回购协议,其中还包括powershell构建脚本

然而,为了避免额外的点击,这里是
clientA.go
的代码

package main

import (
    "fmt"
    "net"
    "time"
)

func main() {
    clientA, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf(":%v", "2222"))
    if err != nil {
        fmt.Println(err)
        return
    }

    clientB, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf(":%v", "3333"))
    if err != nil {
        fmt.Println(err)
        return
    }

    for {
        clientAtoB, err := net.DialTCP("tcp4", clientA, clientB)
        if err != nil {
            fmt.Println(err)
        } else {
            defer clientAtoB.Close()
            clientAtoB.SetLinger(0)
            clientAtoB.SetNoDelay(true)
            clientAtoB.SetKeepAlive(false)
            fmt.Println("connected as Client A!")
            buffer := make([]byte, 64)
            _, err = clientAtoB.Read(buffer)
            if err != nil {
                continue
            }
        }
        time.Sleep(time.Second)
    }
}
clientB.go
的代码相同,但本地和远程端点交换如下:

clientBtoA,err:=net.DialTCP(“tcp4”,clientB,clientA)

问题 我为Windows和Linux构建了相同的go代码,但在运行时应用程序会产生不同的结果。特别是如何在每个平台上拨打TCP连接

在Windows上,当我运行两个可执行文件
clientA.exe
clientB.exe
(从build.ps1脚本构建)时,我得到了所需的结果。如本截图所示:

但是,当我上传并执行Linux二进制文件时,结果不同:

ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$ sudo chmod +x clientA clientB
ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$ ls -la
total 10984
drwxrwxr-x 3 ubuntu ubuntu    4096 Apr 27 03:09 .
drwxrwxr-x 4 ubuntu ubuntu    4096 Apr 27 03:08 ..
drwxrwxr-x 8 ubuntu ubuntu    4096 Apr 27 03:08 .git
-rw-rw-r-- 1 ubuntu ubuntu   11255 Apr 27 03:12 A.txt
-rw-rw-r-- 1 ubuntu ubuntu   11255 Apr 27 03:12 B.txt
-rw-rw-r-- 1 ubuntu ubuntu     247 Apr 27 03:08 build.ps1
-rwxrwxr-x 1 ubuntu ubuntu 2950662 Apr 27 03:08 clientA
-rw-rw-r-- 1 ubuntu ubuntu 2642944 Apr 27 03:08 clientA.exe
-rw-rw-r-- 1 ubuntu ubuntu     718 Apr 27 03:08 clientA.go
-rwxrwxr-x 1 ubuntu ubuntu 2950662 Apr 27 03:08 clientB
-rw-rw-r-- 1 ubuntu ubuntu 2642944 Apr 27 03:08 clientB.exe
-rw-rw-r-- 1 ubuntu ubuntu     718 Apr 27 03:08 clientB.go
ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$ ./clientA > A.txt & ./clientB > B.txt &
[1] 24914
[2] 24915
ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$ cat A.txt
dial tcp4 :2222->:3333: connect: connection refused
ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$ cat B.txt
dial tcp4 :3333->:2222: connect: connection refused
ubuntu@ip-172-31-16-224:~/go/src/github.com/fanmanpro/dial-vs-listen$                                               
我不希望出现
连接被拒绝
错误,因为这两个应用程序在同一个环境下运行,因此没有有效的防火墙,并且权限相同

如何在不考虑平台的情况下获得相同的结果?或者说,为什么它们一开始就不同

编辑 Windows上的成功连接不仅仅是好时机的运气。在Windows上,我可以运行A 5分钟,然后当我运行B时,两者都成功连接

更新(2020-04-27)
在收到Go开发人员的反馈后,我被告知这可能是Linux配置问题,而不是Go特有的。除了权限之外,我不能做任何事情来阻止同一环境中的两个应用程序建立这样的TCP连接?(这些低级Linux的东西并不是我的强项。)

这在Linux上不起作用的原因很明显。A和B都是连接到需要侦听的对应方的客户端。在Linux(或UNIX)上,如果您尝试运行ClientA,它将尝试拨入ClientB的地址和端口。如果没有进程已经监听此地址和端口以接受连接,此时ClientA将立即以
连接被拒绝
错误结束(这并不完全正确,但大多数情况下是这样的,请参阅答案末尾的我的编辑)

在Windows上,Golang使用面向连接的套接字API(用于tcp、tcp4和tcp6协议)。此API的行为与Linux API不同。如果
ConnectEx
无法立即连接,它将返回错误代码
error\u IO\u PENDING
,后台操作系统将等待/重试,直到连接被接受并建立(或放弃并使其最终失败),然后通知返回-这称为重叠I/O

MSDN ConnectEx文件的相关部分:

面向连接的套接字通常无法立即完成连接,因此操作会启动,函数会立即返回错误\u IO\u PENDING或WSA\u IO\u PENDING。当连接操作完成且成功或失败时,将使用lpOverlapped中指示的完成通知机制报告状态

现在,在Windows上发生的情况是,您尝试从两侧
ConnectEx
,操作系统为您连接这些套接字。只有当另一方在一定时间内连接时,这才有效。如果您试图在两个客户端(例如17和28)中合理地增加
时间.睡眠
间隔,您可以看到,即使在Windows上,他们也很难再连接了

对您的问题的回答是,您现在编写的代码取决于Windows上Golang中TCP拨号的操作系统特定行为,因此不可移植。要将您的软件修复为在Golang支持的任何平台上都可移植,您可能需要更改逻辑,以便ClientA和ClientB都可以侦听连接,并定期尝试将连接到另一侧


编辑:我并不是说你的代码根本不能在Linux上运行。它实际上使用了一种罕见的连接模式,称为TCP同步连接,在这种模式下,您可以连接两个进程,而无需任何一个进程
侦听
。拨号双方同时发送他们的SYN,因此每一方用SYN/ACK和ACK进行响应,以完成三方握手并建立连接。这需要在两个客户端中对
connect
调用进行非常精确的定时和同步。如果Linux内核中允许TCP同步连接,并且实现了
connect
s之间的同步,那么双方都会进行连接(仅通过手动或使用同一脚本运行两个客户端很难做到;即使在同一进程和线程中进行模拟也不那么容易)。

这在Linux上不起作用的原因很明显。A和B都是连接到需要侦听的对应方的客户端。在Linux(或UNIX)上,如果您尝试运行ClientA,它将尝试拨入ClientB的地址和端口。如果没有进程已经监听此地址和端口以接受连接,此时ClientA将立即以
连接被拒绝
错误结束(这并不完全正确,但大多数情况下是这样的,请参阅答案末尾的我的编辑)

在Windows上,Golang使用面向连接的套接字API(用于tcp、tcp4和tcp6协议)。此API的行为与Linux API不同。如果
ConnectEx
无法立即连接,它将返回错误代码
error\u IO\u PENDING
,后台操作系统将等待/重试,直到连接被接受并建立(或放弃并使其最终失败),然后通知返回-这称为重叠I/O

MSDN ConnectEx文件的相关部分:

面向连接的套接字通常无法完成其连接