dpg76975 2019-03-26 00:36
浏览 407
已采纳

异常大量的TCP连接超时错误

I am using a Go TCP Client to connect to our Go TCP Server.

I am able to connect to the Server and run commands properly, but every so often there will be an unusually high amount of consecutive TCP connection errors reported by my TCP Client when trying to either connect to our TCP Server or sending a message once connected:

dial tcp kubernetes_node_ip:exposed_kubernetes_port:
connectex: A connection attempt failed because the connected party did not properly
respond after a period of time, or established connection failed because connected
host has failed to respond.

read tcp unfamiliar_ip:unfamiliar_port->kubernetes_node_ip:exposed_kubernetes_port
wsarecv: A connection attempt failed because the connected party did not properly
respond after a period of time, or established connection failed because connected
host has failed to respond.

I say "unusually high" because I assume that the number of times these errors occur should be very minimal (about 5 or less within the hour). Note that I am not dismissing the possibility of this being caused by connection instabilities, as I have also noticed that it is possible to run several commands in rapid succession without any errors.

However, I am still going to post my code in case I am doing something wrong.

Below is the code that my TCP Client uses to connect to our server:

serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
if err != nil {     
    fmt.Println(err)
    return
}

// Never stop asking for commands from the user.
for {
    // Connect to the server.
    serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
    if err != nil {         
        fmt.Println(err)
        continue
    }

    defer serverConnection.Close()

    // Added to prevent connection timeout errors, but doesn't seem to be helping
    // because said errors happen within just 1 or 2 minutes.
    err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
    if err != nil {         
        fmt.Println(err)
        continue
    }

    // Ask for a command from the user and convert to JSON bytes...

    // Send message to server.
    _, err = serverConnection.Write(clientMsgBytes)
    if err != nil {
        err = merry.Wrap(err)
        fmt.Println(merry.Details(err))
        continue
    }

    err = serverConnection.CloseWrite()
    if err != nil {
        err = merry.Wrap(err)
        fmt.Println(merry.Details(err))
        continue
    }

    // Wait for a response from the server and print...
}

Below is the code that our TCP Server uses to accept client requests:

// We only supply the port so the IP can be dynamically assigned:
serverAddress, err := net.ResolveTCPAddr("tcp", ":"+server_port)
if err != nil {     
    return err
}

tcpListener, err := net.ListenTCP("tcp", serverAddress)
if err != nil {     
    return err
}

defer tcpListener.Close()

// Never stop listening for client requests.
for {
    clientConnection, err := tcpListener.AcceptTCP()
    if err != nil {         
        fmt.Println(err)
        continue
    }

    go func() {
        // Add client connection to Job Queue.
        // Note that `clientConnections` is a buffered channel with a size of 1500.
        // Since I am the only user connecting to our server right now, I do not think
        // this is a channel blocking issue.
        clientConnections <- clientConnection
    }()
}

Below is the code that our TCP Server uses to process client requests:

defer clientConnection.Close()

// Added to prevent connection timeout errors, but doesn't seem to be helping
// because said errors happen within just 1 or 2 minutes.
err := clientConnection.SetDeadline(time.Now().Add(10 * time.Minute))
if err != nil {     
    return err
}

// Read full TCP message.
// Does not stop until an EOF is reported by `CloseWrite()`
clientMsgBytes, err := ioutil.ReadAll(clientConnection)
if err != nil {
    err = merry.Wrap(err)
    return nil, err
}

// Process the message bytes...

My questions are:

  1. Am I doing something wrong in the above code, or is the above decent enough for basic TCP Client-Server operations?

  2. Is it okay that both the TCP Client and TCP Server have code that defers closing their one connection?

  3. I seem to recall that calling defer inside a loop does nothing. How do I properly close Client connections before starting new ones?

Some extra information:

  • Said errors are not logged by the TCP Server, so aside from connection instabilities, this might also be a Kubernetes/Docker-related issue.

展开全部

  • 写回答

1条回答 默认 最新

  • donglanzhan7151 2019-03-26 01:18
    关注

    It seems this piece of code does not act as you think it does. The defer statement on the connection close will only happen when the function returns, not when an iteration ends. So as far as I can see here, you are creating a lot of connections on the client side, it could be the problem.

    serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
    if err != nil {     
        fmt.Println(err)
        return
    }
    
    // Never stop asking for commands from the user.
    for {
        // Connect to the server.
        serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
        if err != nil {         
            fmt.Println(err)
            continue
        }
    
        defer serverConnection.Close()
    
        // Added to prevent connection timeout errors, but doesn't seem to be helping
        // because said errors happen within just 1 or 2 minutes.
        err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
        if err != nil {         
            fmt.Println(err)
            continue
        }
    
        // Ask for a command from the user and send to the server...
    
        // Wait for a response from the server and print...
    }
    

    I suggest to write it this way:

    func start() {
        serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
        if err != nil {     
            fmt.Println(err)
            return
        }
        for {
            if err := listen(serverAddress); err != nil {
                fmt.Println(err)
            }
        }
    }
    
    func listen(serverAddress string) error {
         // Connect to the server.
         serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
         if err != nil {         
             fmt.Println(err)
             continue
         }
    
        defer serverConnection.Close()
    
        // Never stop asking for commands from the user.
        for {
            // Added to prevent connection timeout errors, but doesn't seem to be helping
            // because said errors happen within just 1 or 2 minutes.
            err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
            if err != nil {         
               fmt.Println(err)
               return err
            }
    
            // Ask for a command from the user and send to the server...
    
            // Wait for a response from the server and print...
        }
    }
    

    Also, you should keep a single connection open, or a pool of connections, instead of opening and closing the connection right away. Then when you send a message you get a connection from the pool (or the single connection), and you write the message and wait for the response, then you release the connection to the pool.

    Something like that:

    res, err := c.Send([]byte(`my message`))
    if err != nil {
        // handle err
    }
    
    // the implementation of send
    func (c *Client) Send(msg []byte) ([]byte, error) {
        conn, err := c.pool.Get() // returns a connection from the pool or starts a new one
        if err != nil {
            return nil, err
        }
        // send your message and wait for response
        // ...
        return response, nil
    }
    

    展开全部

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
编辑
预览

报告相同问题?

悬赏问题

  • ¥20 校园网认证openwrt插件
  • ¥15 以AT89C51单片机芯片为核心来制作一个简易计算器,外部由4*4矩阵键盘和一个LCD1602字符型液晶显示屏构成,内部由一块AT89C51单片机构成,通过软件编程可实现简单加减乘除。
  • ¥15 某东JD算法逆向算法
  • ¥15 求GCMS辅导数据分析
  • ¥30 SD中的一段Unet下采样代码其中的resnet是谁跟谁进行残差连接
  • ¥15 Unet采样阶段的res_samples问题
  • ¥60 Python+pygame坦克大战游戏开发实验报告
  • ¥15 R语言regionNames()和demomap()无法选中中文地区的问题
  • ¥15 Open GL ES 的使用
  • ¥15 我如果只想表示节点的结构信息,使用GCN方法不进行训练可以吗
手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部