当指定本地端口时,为什么客户端会挂起?

huangapple go评论76阅读模式
英文:

Why does the client hang when the local port is specified?

问题

这涉及到一个必须用Java提交的作业任务。程序按预期工作,将 server.go 的内容打印到终端。为什么在连续运行两次或更多次之后,客户端会停顿30秒钟?

只有在指定客户端端口时才会出现延迟(与作业相关)。

我期望延迟是等待连接关闭的超时,如果不是因为 defer conn.Close() 和客户端仅在前一个客户端返回后才运行。

在延迟期间的 netstat 输出:

$ netstat -anp tcp | grep "8080|8081"
tcp4 0 0 127.0.0.1.8081 127.0.0.1.8080 SYN_SENT
tcp46 0 0 *.8080 . LISTEN

英文:

This is related to a homework assignment that must be submitted in Java. The program works as expected printing the contents of server.go to the terminal. Why does the client hang for 30 seconds after two or more sequential runs?

The delay only occurs when the client port is specified (related to the assignment).

// server.go
package main

import (
	"log"
	"net/http"
)

func main() {
	log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("."))))
}

I'd expect the delay to be a timeout waiting for the connection to close if it were not for defer conn.Close() and the client running only after the previous client returned.

// client.go
package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
)

func main() {
	d := net.Dialer{
		LocalAddr: &net.TCPAddr{
			Port: 8081,
		},
	}

    // Dial the server from client port 8081 to server port 8080
	conn, err := d.Dial("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// Request the resource and log the response
	fmt.Fprint(conn, "GET /server.go HTTP/1.0\r\n\r\n")
	io.Copy(os.Stdout, conn)
}

Output of netstat during a delay:

$ netstat -anp tcp | grep "8080\|8081"
tcp4       0      0  127.0.0.1.8081         127.0.0.1.8080         SYN_SENT   
tcp46      0      0  *.8080                 *.*                    LISTEN    

答案1

得分: 0

我可以重现那个错误。据我所知,这与TCP关闭序列有关,可以参考这个链接 - http://www.tcpipguide.com/free/t_TCPConnectionTermination-4.htm

在OS X上,你可以像这样修改tcp MSL:

sudo sysctl net.inet.tcp.msl=100

所以一个修改过的客户端代码如下:

// client.go
package main

import (
    "fmt"
    "io"
    "log"
    "net"
    "os"
    "time"
)

func check() {
    d := net.Dialer{
        LocalAddr: &net.TCPAddr{
            Port: 8081,
        },
    }

    // 从客户端端口8081拨号到服务器端口8080
    conn, err := d.Dial("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }

    // 请求资源并记录响应
    fmt.Fprint(conn, "GET /server.go HTTP/1.0\r\n\r\n")
    io.Copy(os.Stdout, conn)
    conn.Close()
}

// sudo sysctl net.inet.tcp.msl=100
func main() {

    count := 0
    for {
        fmt.Printf("尝试次数 %d\n", count)
        count++
        check()
        time.Sleep(200 * time.Millisecond)
    }
}

参考链接:https://en.wikipedia.org/wiki/Maximum_segment_lifetime

英文:

I can reproduce that error. AFAICS it has to do with the TCP closing sequence, see this - http://www.tcpipguide.com/free/t_TCPConnectionTermination-4.htm

On OS X you can mess with the tcp MSL like this

sudo sysctl net.inet.tcp.msl=100

So a modified client

// client.go
package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"time"
)

func check() {
	d := net.Dialer{
		LocalAddr: &net.TCPAddr{
			Port: 8081,
		},
	}

	// Dial the server from client port 8081 to server port 8080
	conn, err := d.Dial("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}

	// Request the resource and log the response
	fmt.Fprint(conn, "GET /server.go HTTP/1.0\r\n\r\n")
	io.Copy(os.Stdout, conn)
	conn.Close()
}

// sudo sysctl net.inet.tcp.msl=100
func main() {

	count := 0
	for {
		fmt.Printf("Try num %d\n", count)
		count++
		check()
		time.Sleep(200 * time.Millisecond)
	}
}

See https://en.wikipedia.org/wiki/Maximum_segment_lifetime

huangapple
  • 本文由 发表于 2015年1月26日 05:32:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/28141776.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定