Go client绑定本地IP和端口

有时需要指定网络通信时本地使用的IP地址和端口号。

在Go语言中可通过定义 Dialer 中LocalAddr 成员实现。
Dialer结构定义如下:

// A Dialer contains options for connecting to an address.
//
// The zero value for each field is equivalent to dialing
// without that option. Dialing with the zero value of Dialer
// is therefore equivalent to just calling the Dial function.
type Dialer struct {
    ...

    // LocalAddr is the local address to use when dialing an
    // address. The address must be of a compatible type for the
    // network being dialed.
    // If nil, a local address is automatically chosen.
    LocalAddr Addr
}

Addr是接口类型,其定义如下:

// Addr represents a network end point address.
//
// The two methods Network and String conventionally return strings
// that can be passed as the arguments to Dial, but the exact form
// and meaning of the strings is up to the implementation.
type Addr interface {
    Network() string // name of the network (for example, "tcp", "udp")
    String() string  // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80")
}

目前实现Addr接口的类型并且被net 库支持的类型 包括:TCPAddr、UDPAddr、IPAddr。

下面通过代码演示如何指定client 使用的IP和端口号。

例子中使用TCPAddr 类型。

client


package main


import (

    "bufio"
    "fmt"
    "net"
    "os"
    "time"
)


func DialCustom(network, address string, timeout time.Duration, localIP []byte, localPort int)(net.Conn,error) {
    netAddr := &net.TCPAddr{Port:localPort}

    if len(localIP) != 0 {
        netAddr.IP = localIP
    }

    fmt.Println("netAddr:", netAddr)

    d := net.Dialer{Timeout: timeout, LocalAddr: netAddr}
    return d.Dial(network, address)
}


func main() {

    serverAddr := "172.20.22.160:8080"

    // 172.28.0.180
    //localIP := []byte{0xAC, 0x1C, 0, 0xB4}  // 指定IP
    localIP := []byte{} //  any IP,不指定IP
    localPort := 9001   // 指定端口
    conn, err := DialCustom("tcp", serverAddr, time.Second*10, localIP,localPort)
    if err != nil {
        fmt.Println("dial failed:", err)
        os.Exit(1)
    }
    defer conn.Close()


    buffer := make([]byte, 512)
    reader := bufio.NewReader(conn)

    n, err2 := reader.Read(buffer)
    if err2 != nil {
        fmt.Println("Read failed:", err2)
        return
    }

    fmt.Println("count:", n, "msg:", string(buffer))

    select{}    
}



server

package main

import (
    "fmt"
    "net"
    "log"
)

func main() {

    addr := "0.0.0.0:8080"

    tcpAddr, err := net.ResolveTCPAddr("tcp",addr)

    if err != nil {
        log.Fatalf("net.ResovleTCPAddr fail:%s", addr)
    }

    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        log.Fatalf("listen %s fail: %s", addr, err)
    } else {
    
        log.Println("rpc listening", addr)
    }


    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Println("listener.Accept error:", err)
            continue
        }
    
        go handleConnection(conn)
    
    }

}


func handleConnection(conn net.Conn) {

    //defer conn.Close()

    var buffer []byte = []byte("You are welcome. I'm server.")

    n, err := conn.Write(buffer)

    if err != nil {
    
        fmt.Println("Write error:", err)
    }
    fmt.Println("send:", n)

    fmt.Println("connetion end")

}


测试

启动client,只指定端口

$ ./client 
netAddr: :9001
count: 28 msg: You are welcome. I'm server.

启动client,指定IP,Port

$ ./client
netAddr: 172.28.172.180:9001
count: 28 msg: You are welcome. I'm server

server输出

./sever
2018/06/19 18:15:41 rpc listening 0.0.0.0:8080
send: 28
connetion end

查看连接

$ netstat -anp | grep 8080
tcp        0      0 :::8080                     :::*                        LISTEN      27328/./server      
tcp        0      0 ::ffff:172.20.22.160:8080   ::ffff:172.28.0.180:9001  ESTABLISHED 27328/./server   

从测试结果看,可以成功指定IP和端口。

猜你喜欢

转载自www.cnblogs.com/lanyangsh/p/9202331.html