Go语言IP网络程序设计
来源:云海天教程
时间:2022-12-23 11:57:20 416浏览 收藏
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《Go语言IP网络程序设计》,聊聊网络编程,我们一起来看看吧!
IP 是 Internet 网络层的核心协议,它是一种不可靠的、无连接的通信协议。TCP、UDP 都是在 IP 的基础上实现的通信协议,所以 IP 属于一种底层协议,它可以直接对网络数据包 (Package) 进行处理。另外,通过 IP 用户还可以实现自己的网络服务协议。本节将详细讲解 IP 网络编程服务器、客户机的设计原理和设计过程。IPAddr 地址结构体
在进行 IP 网络编程时,服务器或客户机的地址使用 IPAddr 地址结构体表示,IPAddr 结构体只有一个字段 IP,形式如下:type IPAddr struct {
IP IP
}
函数 ResolveIPAddr() 可以把网络地址转换为 IPAddr 地址结构,该函数原型定义如下:
func ResolveIPAddr(net, addr string) (*IPAddr, error)
在调用 ResolveIPAddr() 函数时,参数 net 表示网络类型,可以是“ip”、“ip4”或“ip6”,参数 addr 是 IP 地址或域名,如果是 IPv6 地址则必须使用“[]”括起来。函数 ResolveIPAddr() 调用成功后返回一个指向 IPAddr 结构体的指针,否则返回一个错误类型。
另外,IPAddr 地址对象还有两个方法:Network() 和 String()。Network() 方法用于返回 IPAddr 地址对象的网络协议名,比如“ip”;String() 方法可以将 IPAddr 地址转换成字符串形式。这两个方法原型定义如下:
func (a *IPAddr) Network() string
func (a *IPAddr) String() string
IPConn 对象
在进行 IP 网络编程时,客户机和服务器之间是通过 IPConn 对象实现连接的,IPConn 是 Conn 接口的实现。IPConn 对象绑定了服务器的网络协议和地址信息,IPConn 对象定义如下:type IPConn struct {
//空结构
}
func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error)
func (c *IPConn) WriteToIP(b []bytez addr *IPAddr) (int, error)
IP 服务器设计
由于工作在网络层,ip 服务器并不需要在一个指定的端口上和客户机进行通信连接,IP 服务器的工作过程如下:1) IP 服务器使用指定的协议簇和协议,调用 ListenIP() 函数创建一个 IPConn 连接对象,并在该对象和客户机间建立不可靠连接。
2) 如果服务器和某个客户机建立了 IPConn 连接,就可以使用该对象的 ReadFromIP() 方法和 WriteToIP() 方法相互通信了。
3) 如果通信结束,服务器还可以调用 Close() 方法关闭 IPConn 连接。
函数 ListenIP() 原型定义如下:
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error)
在调用函数 ListenIP() 时,参数 netProto 是“网络类型+协议名”或“网络类型+协议号”,中间用“:”隔开,比如“IP4:IP”或“IP4:4”。参数 laddr 是服务器本地地址,可以是任意活动的主机地址,或者是内部测试地址“127.0.0.1”。该函数调用成功,返回一个 IPConn 对象;调用失败,返回一个错误类型。【示例 1】IP Server 端设计,服务器使用本地主机地址,调用 Hostname() 函数获取。服务器设计工作模式采用循环服务器,对每一个连接请求调用线程 handleClient 来处理。
// IP Server 端设计package mainimport( "fmt" "net" "os")func main() { name, err := os.Hostname() checkError(err) ipAddr, err := net.ResolveIPAddr("ip4", name) checkError(err) fmt.Println(ipAddr) conn, err := net.ListenIP("ip4:ip", ipAddr) checkError(err) for { handleClient(conn) }}func handleClient(conn *net.IPConn) { var buf [512]byte n, addr, err := conn.ReadFromIP(buf[0:]) if err != nil { return } fmt.Println("Receive from client", addr.String(), string(buf[0:n])) conn.WriteToIP([]byte("Welcome Client!"), addr)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) }}
IP 客户机设计
在 ip 网络编程中,客户机工作过程如下:1) IP 客户机在获取了服务器的网络地址之后,可以调用 DialIP() 函数向服务器发出连接请求,如果请求成功会返回 IPConn 对象。
2) 如果连接成功,客户机可以直接调用 IPConn 对象的 ReadFromIP() 方法或 WriteToIP() 方法,与服务器进行通信活动。
3) 通信完成后,客户机调用 Close() 方法关闭 IPConn 连接,断开通信链路。
函数 DialIP() 原型定义如下:
func DialIP (netProto string, laddr, raddr *IPAddr) (*IPConn, error)
在调用函数 DialIP() 时,参数 netProto 是“网络类型+协议名”或“网络类型+协议号”,中间用“:”隔开,比如“IP4:IP”或“IP4:4”。参数 laddr 是本地主机地址,可以设为 nil。参数 raddr 是对方主机地址,必须指定不能省略。函数调用成功后,返回 IPConn 对象;调用失败,返回一个错误类型。方法 Close() 的原型定义如下:
func (c *IPConn) Close() error
该方法调用成功后,关闭 IPConn 连接;调用失败,返回一个错误类型。【示例 2】IP Client 端设计,客户机通过内部测试地址“127.0.0.1”和服务器建立通信连接,服务器主机地址可以使用 Hostname() 函数获取。
// IP Client端设计package mainimport( "fmt" "net" "os")func main() { if len(os.Args) != 2 { fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0]) } service := os.Args[1] lAddr, err := net.ResolveIPAddr("ip4", service) checkError(err) name, err := os.Hostname() checkError(err) rAddr, err := net.ResolveIPAddr("ip4", name) checkError(err) conn, err := net.DialIP("ip4:ip", lAddr, rAddr) checkError(err) _, err = conn.WriteToIP([]byte("Hello Server!"), rAddr) checkError(err) var buf [512]byte n, addr, err := conn.ReadFromIP(buf[0:]) checkError(err) fmt.Println("Reply form server", addr.String(), string(buf[0:n])) conn.Close() os.Exit(0)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) }}编译并运行服务器端和客户端,测试过程如下:
启动服务器:go run .main.go
客戶机连接:go run .client.go 127.0.0.1
服务器响应:Receive from client 127.0.0.1 Hello Server!
客户机接收:Reply form server 192.168.1.104 Welcome Client!
如果使用相同的地址,会发生自发自收的现象,原因是 IP 是底层通信,并没有像 TCP、UDP 那样使用端口号来区分不同的通信进程。
Ping 程序设计
不管是 UNIX 还是 Windows 系统中都有一个 Ping 命令,利用它可以检查网络是否连通,分析判断网络故障。Ping 会向目标主机发送测试数据包,看对方是否有响应并统计响应时间,以此测试网络。Ping 命令的这些功能是使用 IP 层的 ICMP 实现的,在测试过程中,源主机向目标主机发送回显请求报文(ICMP_ECHO_REQUEST,type = 8, code = 0),目的主机返回回显响应报文(ICMP_ECHO_REPLY,type = 0, code = 0),相关的数据包格式如下图所示。
图:ICMP 回显请求和响应数据包格式
其中,标识符是源主机的进程号,序列码用来标识发出回显请求的次序,时间戳表示数据包发出的时刻,通过比较回显响应时刻和源主机当前时刻的差值,可以测出 ICMP 数据包的往返时间。
【示例 3】使用原始套接字和 ICMP 设计 Ping 程序,函数 makePingRequest() 的功能是生成 ICMP 请求包,函数 parsePingReply() 用于解析目标主机发回的响应包,函数 elapsedTime() 的功能是计算 ICMP 数据包往返时间。
// Ping 程序package mainimport( "bytes" "fmt" "net" "os" "time")const( ICMP_ECHO_REQUEST = 8 ICMP_ECHO_REPLY = 0)func main() { if len(os.Args) != 2 { fmt.Fprintf(os.Stderr, "Usage: %s host", os.Args[0]) os.Exit(1) } dst := os.Args[1] raddr, err := net.ResolveIPAddr("ip4",dst) checkError(err) ipconn, err := net.DialIP("ip4:icmp", nil, raddr) checkError(err) sendid := os.Getpid() & 0xfff sendseq := 1 pingpktlen := 64 for { sendpkt := makePingRequest(sendid, sendseq, pingpktlen, []byte("")) start := int64(time.Now().Nanosecond()) _, err := ipconn.WriteToIP(sendpkt, raddr) checkError(err) resp := make([]byte, 1024) for { n, from, err := ipconn.ReadFrom(resp) checkError(err) fmt.Printf("%d bytes from %s: icmp_req = %d time = %.2f ms", n, from, sendseq, elapsedTime(start)) if resp[0] != ICMP_ECHO_REPLY { continue } rcvid, rcvseq := parsePingReply(resp) if rcvid != sendid || rcvseq != sendseq { fmt.Printf("Ping reply saw id", rcvid, rcvseq, sendid, sendseq) } break } if sendseq == 4 { break } else { sendseq++ } time.Sleep(1e9) }}func makePingRequest(id, seq, pktlen int, filler []byte) []byte { p := make([]byte, pktlen) copy(p[8:], bytes.Repeat(filler,(pktlen - 8)/len(filler) + 1)) p[0] = ICMP_ECHO_REQUEST // type p[1] = 0 // cksum p[2] = 0 // cksum p[3] = 0 // id p[4] = uint8(id >> 8) // id p[5] = uint8(id & 0xff) // id p[6] = uint8(seq >> 8) // sequence p[7] = uint8(seq & 0xff) // sequence cklen := len(p) s := uint32(0) for i := 0; i > 16) + (s & 0xffff) s = s + (s >> 16) p[2] ^= uint8(^s & 0xff) p[3] ^= uint8(^s >> 8) return p}func parsePingReply(p []byte) (id, seq int) { id = int(p[4]) 编译并运行Ping程序,测试结果如下:
Ping www.sina.com.cn
64 bytes from 218.30.66.101: icmp_req = 1 time = 15 ms
64 bytes from 218.30.66.101: icmp_req = 2 time = 12 ms
64 bytes from 218.30.66.101: icmp_req = 3 time = 15 ms
64 bytes from 218.30.66.101: icmp_req = 4 time = 15 ms
Ping www.google.com.hk
64 bytes from 125.76.239.243: icmp_req = 1 time = 31 ms
64 bytes from 125.76.239.243: icmp_req = 2 time = 15 ms
64 bytes from 125.76.239.243: icmp_req = 3 time = 23 ms
64 bytes from 125.76.239.243: icmp_req = 4 time = 15 ms
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。
-
215 收藏
-
399 收藏
-
458 收藏
-
151 收藏
-
169 收藏
-
202 收藏
-
199 收藏
-
145 收藏
-
168 收藏
-
165 收藏
-
473 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习