net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket.
虽然本包提供了对网络原语的访问,但大多数使用者只需要Dial、Listen和Accpet函数的基本接口;以及Conn和Listener接口。crypto/tls包提供了相同的接口和类似的Dial和Listen函数。
//表示ip地址的长度(bytes),其中ipv4的长度为4,ipv6为16
const ( IPv4len = 4 IPv6len = 16 )
常见的ipv4地址:
var (
IPv4bcast = IPv4(255, 255, 255, 255) // 广播地址
IPv4allsys = IPv4(224, 0, 0, 1) // 所有系统,包括主机和路由器,这是一个组播地址
IPv4allrouter = IPv4(224, 0, 0, 2) // 所有组播路由器
IPv4zero = IPv4(0, 0, 0, 0) // 本地网络,只能作为本地源地址其才是合法的
)
常见的IPV6地址
var (
IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
IPv6unspecified = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
IPv6interfacelocalallnodes = IP{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
IPv6linklocalallnodes = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
IPv6linklocalallrouters = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}
)
IP地址(Internet Protocol Address,称互联网协议地址,简称IP Address),是一种给主机在Internet上进行编址的方式。常见的地址分IPV4和IPV6。
IPV4是由32位二进制数表示。用XXX.XXX.XXX.XXX表示。每组XXX小于或等于255的10进制数,IPV4地址总数为232,不过一些地址是为特殊用途所保留的,专用网络(约18百万)和多播地址(270百万)。其中IPV4分ABCDE五类地址。
第一位为0,主要的是位
1.位1,第1字节为网络地址,其他3个字节为主机地址
2.地址范围: 1.0.0.1—126.255.255.254
3.10.X.X.X为私有地址,范围是10.0.0.0-10.255.255.255
4.127.X.X.X是保留地址,用做环回测试
其两位为10,注意的是位。
1.第1和第2字节为网络地址,后2字节为主机地址。
2.地址范围: 128.0.0.1—191.255.255.254
3.私有地址范围: 172.16.0.0—172.31.255.255
4.保留地址:169.254.X.X
前三位为110,注意的是位。
1.前3字节为网络地址,后字节为主机地址
2.地址范围:192.0.0.1—223.255.255.254
3.私有地址: 192.168.X.X,范围从192.168.0.0-192.168.255.255
前四位为1110,注意3位。
1.不分网络地址和主机地址。
前五位为11110。
1.不分网络地址和主机地址。
1、0.0.0.0只能做源地址
2、255.255.255.255是广播地址
3、127.x.x.x为环回地址,本机使用
专用地址就是内网地址
10/8 地址范围:10.0.0.0——10.255.255.255,
172.16/12 地址范围:172.16.0.0——172.31.255.255,
192.168/16地址范围:192.168.0.0——192.168.255.255。
一些小型企业或者学校,通常都是申请一个固定的IP地址,然后通过IP共享(IP Sharing),使用整个公司或学校的机器都能够访问互联网。而这些企业或学校的机器使用的IP地址就是内网IP,内网IP是在规划IPv4协议时,考虑到IP地址资源可能不足,就专门为内部网设计私有IP地址(或称之为保留地址)。一般常用内网IP地址都是这种形式的:10.X.X.X、172.16.X.X-172.31.X.X、192.168.X.X等。需要注意的是,内网的计算机可向Internet上的其他计算机发送连接请求,但Internet上其他的计算机无法向内网的计算机发送连接请求。我们平时可能在内网机器上搭建过网站或者FTP服务器,而在外网是不能访问该网站和FTP服务器的,原因就在于此。
公网IP就是除了保留IP地址以外的IP地址,可以与Internet上的其他计算机随意互相访问。我们通常所说的IP地址,其实就是指的公网IP。互联网上的每台计算机都有一个独立的IP地址,该IP地址唯一确定互联网上的一台计算机。这里的IP地址就是指的公网IP地址。
怎样理解互联网上的每台计算机都有一个唯一的IP地址?
其实,互联网上的计算机是通过“公网IP+内网IP”来唯一确定的。就像很多大楼都是201房间一样,房间号可能一样,但是大楼肯定是唯一的。公网IP地址和内网IP地址也是同样,不同企业或学校的机器可能有相同的内网IP地址,但是他们的公网IP地址肯定不同。那么这些企业或学校的计算机是怎样进行IP地址共享的呢?这就需要使用NAT(Network Address Translation,网络地址转换)功能。当内部计算机要连接互联网时,首先需要通过NAT技术,将内部计算机数据包中有关IP地址的设置都设成NAT主机的公共IP地址;然后再传送到Internet,虽然内部计算机使用的是私有IP地址,但在连接Internet时,就可以通过NAT主机的NAT技术,将内网IP地址修改为公网IP地址,如此一来,内网计算机就可以向Internet请求数据了。
type Error //表示网络错误
type Error interface {
error //错误
Timeout() bool // Is the error a timeout? 该错误是时间超时错误吗?
Temporary() bool // Is the error temporary? 这个错误是一个临时错误吗?
}
type AddrError //网络地址错误
type AddrError struct {
Err string //错误
Addr string //地址字符串表示
}
func (e *AddrError) Error() string //错误
func (e *AddrError) Temporary() bool //该错误是否是一个临时错误
func (e *AddrError) Timeout() bool //该错误是否是超时错误
type DNSConfigError //DNS配置错误,表示在读取机器DNS配置过程中的出现的错误
type DNSConfigError struct {
Err error
}
func (e *DNSError) Error() string
func (e *DNSError) Temporary() bool
func (e *DNSError) Timeout() bool
type InvaildAddrError //无效地址错误
type InvalidAddrError string
func (e InvalidAddrError) Error() string
func (e InvalidAddrError) Temporary() bool
func (e InvalidAddrError) Timeout() bool
type ParseError //解析错误,ParseError表示一个格式错误的字符串,其中Type表示期待的格式
type ParseError struct {
Type string
Text string
}
func (e *ParseError) Error() string //将错误表示为字符串形式
type UnknownNetworkError //未知网络错误
type UnknownNetworkError string
func (e UnknownNetworkError) Error() string
func (e UnknownNetworkError) Temporary() bool
func (e UnknownNetworkError) Timeout() bool
//网络终端地址
type Addr interface {
Network() string // 网络名称
String() string // 地址字符串表示
}
//conn是一个通用的面向流的网络连接,多个goroutine可以调用conn中的方法
type Conn interface {
// Read从连接中读取数据,Read方法会在超过某个固定时间限制后返回一个表示超时的错误,该错误的TImeout()==True
Read(b []byte) (n int, err error)
// Write向conn中写入数据,与Read类似, Write方法也会在超过某个固定时间后返回超时错误,该错误的Timeout()==True
Write(b []byte) (n int, err error)
// Close方法关闭该连接,同时任何阻塞的Read或Write方法将不再阻塞,并且返回错误。
Close() error
// 返回本地网络地址
LocalAddr() Addr
// 返回远端网络地址
RemoteAddr() Addr
// 设定连接的读写deadline,它等价于调用SetReadDeadline和SetWriteDeadline进行conn连接的读写deadline设定。其中deadline是一个绝对时间,
//在deadline时间之后,任何的读写操作都不再阻塞,而是直接失败,deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作,
// :一:空闲超时,这种方法实现其中有两类超时比较特殊是在每次成功读或者写操作后都延长超时期限,当没有读写操作空闲时便会超时;二:参数t为零值表示不设置
//超时期限,即不会超时timeout
SetDeadline(t time.Time) error
// 设定连接的读操作deadline,参数t为零值表示不设置期限
SetReadDeadline(t time.Time) error
// 设定连接的写操作deadline,参数t为零值表示不设置期限
// 即使写超时,也有可能出现写入字数n>0,说明成功写入了部分数据,但是没有将数据全部写入。
SetWriteDeadline(t time.Time) error
}
//Dial连接到指定address和name的网络,其中network包含如下几种:"tcp", "tcp4" (IPv4-only), //"tcp6" (IPv6-only),"udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"(IPv4-only), "ip6" //(IPv6-only), "unix", "unixgram" and"unixpacket". 对于TCP和UDP网络来说,addresses的形式 //如下host:port,其行使和JoinHostPort以及SplitHostPort函数中的addresses形式一致。举例如下所示:
Dial("tcp", "12.34.56.78:80")
Dial("tcp", "google.com:http")
Dial("tcp", "[2001:db8::1]:http")
Dial("tcp", "[fe80::1%lo0]:80")
func Dial(network, address string) (Conn, error)
//该函数与Dial函数类似,但是多一个超时设置timeout,如果需要的话,timeout中包含域名解析
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
//包含连接到一个地址的选项,//在Dialer结构体中的每个参数的零值相当于没有那个值,因此调用
//零值的Dialer中的Dial函数相当于直接调用Dial函数
type Dialer struct {
// Timeout是dial操作等待连接建立的最大时长,默认值代表没有超时。如果Deadline字段也被设置了,dial操作也可能更早失败。
// 不管有没有设置超时,操作系统都可能强制执行它的超时设置。例如,TCP(系统)超时一般在3分钟左右。Timeout是一个相对时间,是时间段,而Deadline是一个绝
//对时间,是时间点,这是二者的区别
Timeout time.Duration
// Deadline是一个具体的时间点期限,超过该期限后,dial操作就会失败。如果Timeout字段也被设置了,dial操作也可能更早失败。零值表示没有期限。
Deadline time.Time
// LocalAddr是dial一个地址时使用的本地地址。
// 该地址必须是与dial的网络相容的类型。
// 如果为nil,将会自动选择一个本地地址。
LocalAddr Addr
// 当网络类型是tcp并且一个主机名字具有多个dns记录地址时,DualStack允许一个dial创建多个ipv4和ipv6的连接,并且返回第一个创建的连接
DualStack bool
// KeepAlive指定一个网络连接的保持声明的时间段;如果为0,会禁止keep-alive。当网络协议不支持keep-alives时便会忽略掉这个值。
// 不支持keep-alive的网络连接会忽略本字段。
KeepAlive time.Duration
}
//Listener是一个用于面向流的网络协议的公用的网络监听器接口。多个线程可能会同时调用一个Listener的方法。
type Listener interface {
Accept() (c Conn, err error)//等待并返回下一个连接到该连接的连接
Close() error //关闭listener,关闭后,任何阻塞accept的操作都不再阻塞,并且返回error
Addr() Addr //返回该接口的网络地址
}
func getExternalIp() string {
resp, err := http.Get("https://myexternalip.com/raw")
if err != nil {
return ""
}
defer resp.Body.Close()
content, _ := ioutil.ReadAll(resp.Body)
//buf := new(bytes.Buffer)
//buf.ReadFrom(resp.Body)
//s := buf.String()
return string(content)
}
func IsPublicIP(IP net.IP) bool {
if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
return false
}
if ip4 := IP.To4(); ip4 != nil {
switch true {
case ip4[0] == 10:
return false
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
return false
case ip4[0] == 192 && ip4[1] == 168:
return false
default:
return true
}
}
return false
}
func getLocalIPv4s() ([]string, error) {
var ips []string
addrs, err := net.InterfaceAddrs()
if err != nil {
return ips, err
}
for _, a := range addrs {
// 检查ip地址判断是否回环地址
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil {
ips = append(ips, ipNet.IP.String())
}
}
return ips, nil
}
func GetIPv4ByInterface(name string) ([]string, error) {
var ips []string
netInterface, err := net.InterfaceByName(name)
if err != nil {
return nil, err
}
mac物理地址
//fmt.Println(netInterface.HardwareAddr.String())
addrs, err := netInterface.Addrs()
if err != nil {
return nil, err
}
for _, a := range addrs {
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil {
ips = append(ips, ipNet.IP.String())
}
}
return ips, nil
}
func GetLocalIP() string {
conn, _ := net.Dial("udp", "8.8.8.8:80")
defer conn.Close()
localAddr := conn.LocalAddr().String()
idx := strings.LastIndex(localAddr, ":")
return localAddr[0:idx]
}
func inet_aton(ipnr net.IP) int64 {
bits := strings.Split(ipnr.String(), ".")
b0, _ := strconv.Atoi(bits[0])
b1, _ := strconv.Atoi(bits[1])
b2, _ := strconv.Atoi(bits[2])
b3, _ := strconv.Atoi(bits[3])
var sum int64
sum += int64(b0) << 24
sum += int64(b1) << 16
sum += int64(b2) << 8
sum += int64(b3)
return sum
}
func inet_ntoa(ipnr int64) net.IP {
var byteSlice [4]byte
byteSlice[0] = byte(ipnr & 0xFF)
byteSlice[1] = byte((ipnr >> 8) & 0xFF)
byteSlice[2] = byte((ipnr >> 16) & 0xFF)
byteSlice[3] = byte((ipnr >> 24) & 0xFF)
return net.IPv4(byteSlice[3], byteSlice[2], byteSlice[1], byteSlice[0])
}
func IpBetween(from net.IP, to net.IP, test net.IP) bool {
if from == nil || to == nil || test == nil {
fmt.Println("An ip input is nil") // or return an error!?
return false
}
from16 := from.To16()
to16 := to.To16()
test16 := test.To16()
if from16 == nil || to16 == nil || test16 == nil {
fmt.Println("An ip did not convert to a 16 byte") // or return an error!?
return false
}
if bytes.Compare(test16, from16) >= 0 && bytes.Compare(test16, to16) <= 0 {
return true
}
return false
}
func GetFreePort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}