我们在日常开发当中,几乎所有网络编程都是
Socket
编程,因为大部分底层网络的编程都离不开Socket
编程。
什么是Socket
其实Socket
起源于UNIX
,网络的Socket
数据传输是一种特殊的I/O
,Socket
也是一种文件描述符。
同文件的基本操作一样,Socket
也有一个类似的函数--Socket()
,该函数返回的是一个整型的Socket
描述符,之后再进行类似于连接建立、数据传输等操作就都可以通过这个Socket
来实现了。
我们知道,在计算机网络当中,存在着TCP
和UDP
两种协议,因此Socket
也相应的对应着两种类型:流式Socket
(SOCK_STREAM
),和数据报式Socket
(SOCK_DGRAM
),其中流式Socket
是一种面向连接的Socket
,针对的是面向连接的TCP
服务应用,数据报式Socket
是一种无连接的Socket
,针对的是面向无连接的UDP
服务应用。
那么在建立一个Socket
连接时,除了我们知道它是应用了哪种协议后,还有两个内容是需要确定的,就是IP地址和端口。
Go语言所支持的IP类型
Go语言针对网络编程定义了net
包,里面有很多关于网络编程的类型,函数和方法。其中IP
的定义如下:
type IP []byte
虽然在net
包中有很多的函数用于操作IP
,但我们在这里只介绍几个比较常用的。
先来看第一个:ParseIP(s string)
,这个函数会把一个IPv4
或IPv6
的地址转化为IP
类型:
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Useage: %s IP Address\n", os.Args[0])
os.Exit(1)
}
ipAddress := os.Args[1]
addr := net.ParseIP(ipAddress)
if addr == nil {
fmt.Println("Invalid IP Address!")
} else {
fmt.Println("The IP Address is: ", addr.String())
}
os.Exit(0)
}
只要我们在执行时给定一个标准的IP地址
,就会给出相应的IP格式
。
Dial()函数
在Go语言中,通常使用Dial()
函数来连接服务器,Listen()
进行监听,Accept()
接收连接。
Go语言标准库对传统的Socket
编程过程进行了抽象和封装,无论期望使用什么协议建立什么形式的连接,都只需要调用net.Dial()
即可。
Dial()
函数的原型如下:
func Dial(net, addr string) (Conn, error)
net
参数是网络协议的名字,addr
参数是IP
地址或者域名,而端口号以:
的形式跟随在地址或域名的后面,端口号可选。
几种常见协议的调用方式:
TCP
:
conn, err := net.Dial("tcp", "192.168.0.10:8888")
UDP
:
conn, err := net.Dial("udp", "192.168.0.10:8888")
ICMP
:
/** 使用协议名称 */
conn, err := net.Dial("ip4:icmp", "www.baidu.com")
/** 使用协议编号 */
conn, err := net.Dial("ip4:1", "10.0.0.1")
在连接建立成功之后,就可以进行数据的发送与接收了,其中发送使用的是Write()
函数,接收使用的是Read()
函数。
package main
import (
"bytes"
"fmt"
"io"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "hostname")
os.Exit(1)
}
service := os.Args[1]
conn, err := net.Dial("ip4:icmp", service)
checkError(err)
var msg [512]byte
// ICMP报文
msg[0] = 8 // echo
msg[1] = 0 // code 0
msg[2] = 0 // checksum
msg[3] = 0 // checksum
msg[4] = 0 // identifier[0]
msg[5] = 13 // identifier[1]
msg[6] = 0 // sequence[0]
msg[7] = 37 // sequence[1]
len := 8
check := checkSum(msg[0:len])
msg[2] = byte(check >> 8)
msg[3] = byte(check & 255)
_, err = conn.Write(msg[0:len])
checkError(err)
_, err = conn.Read(msg[0:])
checkError(err)
fmt.Println("Get response...")
if msg[5] == 13 {
fmt.Println("Identifier match.")
}
if msg[7] == 37 {
fmt.Println("Sequence match.")
}
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
os.Exit(1)
}
}
func checkSum(msg []byte) uint16 {
sum := 0
for n := 1; n < len(msg)-1; n += 2 {
sum += int(msg[n])*256 + int(msg[n+1])
}
sum = (sum >> 16) + (sum & 0xffff)
sum += (sum >> 16)
var answer uint16 = uint16(^sum)
return answer
}
func readFully(conn net.Conn) ([]byte, error) {
defer conn.Close()
result := bytes.NewBuffer(nil)
var buf [512]byte
for {
_, err := conn.Read(buf[0:])
result.Write(buf[0:])
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
}
return result.Bytes(), nil
}
运行程序后,如果取得响应,就会返回相应的结果,如下:
Get response...
Identifier match.
Sequence match.
网友评论