Go 网络编程笔记
前言: 本文是学习<<go语言程序设计>> -- 清华大学出版社(王鹏 编著) 的2014年1月第一版 做的一些笔记 , 如有侵权, 请告知笔者, 将在24小时内删除, 转载请注明出处!
1. 标准库中提供net包, 支持基于网络层(IP),传输层(TCP/UDP) 以及应用层(如HTTP,FTP,SMTP) 的网络通信.
2. IP 地址和域名解析
- IP地址类型: type IP[] byte
- 常用函数
- func ParseIP(s string) IP 验证IP的合法性, 返回IP地址对象或 nil.
- func (ip IP) String() string 将IPv4转换成点分十进制格式的字符串, IPv6转换成冒号分隔的简写字符串.
package main import (
"fmt"
"net"
) func main() {
addr := "192.168.0.1"
ip := net.ParseIP(addr)
if ip == nil {
fmt.Println("IP Invalid!")
} else {
fmt.Println("IP is: ", ip.String())
}
}
- IPMask地址类型: type IPMask [] byte (只有IPv4才有默认的子网掩码)
- 常用函数
- func IPv4Mask(a,b,c,d byte) IPMask 通过32位的IPv4地址生成子网掩码地址并返回.
- func (ip IP) DefaultMask() IPMask 返回IPv4的默认子网掩码, 否则返回 nil
- func (m IPMask) Size() (ones,bits int) 通过子网掩码对象返回掩码位数(ones) 和掩码总长度(bits). 如果是非标准的子网掩码, 将返回0,0.
- func (ip IP) Mask(mask IPMask) IP 返回IP地址与子网掩码相与的结果, 即网络地址.
package main import (
"fmt"
"net"
) func main() {
ip := net.ParseIP("192.168.0.1")
if ip == nil {
fmt.Println("Invalid IP!")
}
mask := ip.DefaultMask()
fmt.Println("Subnet mask is:", mask)
network := ip.Mask(mask)
fmt.Println("Network address is:", network)
ones, bits := mask.Size()
fmt.Println("Mask ones:", ones, " Total Mask Len is:", bits)
}
- IPAddr 结构体类型
- 定义: type IPAddr struct { IP IP} , 许多函数和方法都会返回一个指向这个结构体的指针.
- IPAddr主要作用是用于域名解析服务(DNS)
- 常用函数
- func ResolveIPAddr(net, addr string)(*IPAddr,error) net 表示网络类型(ip,ip4,ip6), addr为IP地址或者域名.
- 域名解析
package main import (
"fmt"
"net"
"os"
) func main() {
addr := "www.baidu.com"
ip, err := net.ResolveIPAddr("ip", addr)
if err != nil {
fmt.Println("Resolvation error:#", err.Error())
os.Exit(1)
}
fmt.Println("Resolved address is:", ip.String())
}
3. 主机信息查询
- 大多数网络中的主机都有多个IP地址, 所以用主机名来获取一个主机的IP地址有事不好用. 一台主机也可能有别名.
- 常用函数
- func LookupHost(host string) (addrs []string, err error) 返回主机地址列表或错误.
package main import (
"fmt"
"net"
) func main() {
name := "www.baidu.com"
addrs, err := net.LookupHost(name)
if err != nil {
fmt.Println("LookupHost Error:#", err.Error())
} else {
for _, s := range addrs {
fmt.Println(s)
}
}
}
- func LookupCNAME(name string) (cname string, err error) 查询主机正式名.
4. 服务信息查询
- 查询 SRV记录
- 服务端口查询
5. Go 网络编程
- 传统的Socket编程
-流式套接字步骤:
- 服务器和客户端分别调用socket()创建套接字
- 服务器和客户端分别调用bind()函数绑定服务器地址
- 服务器调用liste()监听, 客户机调用connect()进行连接并向服务器发出连接请求.
- 服务器调用accept()接受请求并重新创建一个套接字用于和客户机之间通信连接.
- 调用send() 和recv() 收发数据.
- 任何一方可调用close()关闭
- 数据报套接字不需监听和连接步骤, 服务器和客户机只要建立好套接字后就可以相互收发数据.
- Go中的网络编程.
- 对传统的Socket编程进行了封装. 无论想用什么形式的连接, 都用 func Dial(net,addr string) (Conn,error) net是协议名(如tcp,ip), addr为IP地址或域名, 后加 :端口号. 如: conn, err := net.Dial("tcp","192.168.0.1:5000") conn, err := net.Dial("ip4:icmp","www.baidu.com")
- Dail()支持以下几种协议: tcp,tcp4,tcp6, udp,udp4,udp6,ip,ip4,ip6
- 成功建立连接之后就可以进行数据收发了.
6.TCP
- TCP工作在传输层, 面向连接, TCP程序设计属于C/S模式.
type TCPAddr struct{
IP IP
Port int
}
- func ResolveTCPAddr(net,addr string)(*TCPAddr,error) 将网络地址转换成TCPAddr地址结构. www.google.com:80
- net : 网络协议名, 可以是 tcp,tcp4,tcp6
- addr: IP地址或者域名, 如果是IPv6 必须用 " [ ] " 括起来, 端口号以 ":port" 的形式放在后面
- 常用方法:
- func (a * TCPAddr) Network() string 返回TCPAddr地址对象的网络协议名, 如"tcp"
- func (a * TCPAddr) String() string 将TCPAddr转换成字符串形式.
package main import (
"fmt"
"net"
) func main() {
networkType := "tcp4"
addr := "www.baidu.com:80"
tcpAddr, err := net.ResolveTCPAddr(networkType, addr)
if err != nil {
fmt.Println("ResolveTCPAddr error:", err.Error())
} else {
fmt.Println("The Network Type is: ", tcpAddr.Network(), "IP is: ", tcpAddr.String())
}
}
- TCPConn 对象
- TCP编程中客户机和服务器之间的通信是通过TCPConn对象实现连接的.
- TCPConn是Conn接口的实现, 绑定了服务器的网络协议和地址信息.
type TCPConn struct{
// 定义为空结构
}
- 通过TCPConn连接对象, 可以实现客户机和服务器之间的全双工通信. 可以通过TCPConn的Read()/Write() 收发数据, 这两个方法都会引起阻塞.
- func (c * TCPConn) Read(b []byte) (n int, err error)
- func (c * TCPConn) Write(b []byte) (n int, err error)
- TCP Server
- TCP Server先注册一个共有端口号, 然后调用ListenTCP() 在这个端口上创建一个TCPListener监听对象, 并监听连接请求.
- 启用TCPListener对象的Accept()接收请求, 返回一个协议相关的Conn对象(这里对应的就是TCPConn对象)
- 如果返回一个新的TCPConn对象 , 服务器就可以像调用该对象的Read()/Write() 方法进行收发数据.
type TCPListener struct{
// contains filtered or unexported fields.
}
func ListenTCP(net string, laddr * TCPAddr)(*TCPListener, error)
- net : tcp / tcp4 / tcp6
- laddr: local server address
- func (l * TCPListener) Accept()(c Conn,err error) 返回TCPConn(实现了Conn接口)对象.
- 通信过程中如果想获取通信地址
- func (c * TCPConn) LocalAddr/RemoteAddr() Addr
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
tcpAddr, err := net.ResolveTCPAddr("tcp", ":5000")
checkError(err)
tcpListener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
tcpConn, err := tcpListener.Accept()
if err != nil {
continue
}
handleClient(tcpConn)
tcpConn.Close()
}
} func handleClient(conn net.Conn) {
var buf [512]byte
for {
n, err := conn.Read(buf[0:])
if err != nil {
return
}
rAddr := conn.RemoteAddr()
fmt.Println("Receive from client:", rAddr.String(), "#", string(buf[0:n]))
_, err = conn.Write([]byte("Welcome client!"))
if err != nil {
return
}
}
}
- 并发的Server
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
tcpAddr, err := net.ResolveTCPAddr("tcp", ":5000")
checkError(err)
tcpListener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
tcpConn, err := tcpListener.Accept()
if err != nil {
continue
}
go handleClient(tcpConn)
}
} func handleClient(conn net.Conn) {
defer conn.Close()
var buf [512]byte
for {
n, err := conn.Read(buf[0:])
if err != nil {
return
}
rAddr := conn.RemoteAddr()
fmt.Println("Receive from client:", rAddr.String(), "#", string(buf[0:n]))
_, err = conn.Write([]byte("Welcome client!"))
if err != nil {
return
}
}
}
- TCP Client
- 工作流程
- 获取服务器地址和端口号之后, 调用DialTCP()函数向服务器发出连接请求, 如果请求成功会返回TCPConn对象
- 调用 TCPConn 对象的 Read()和Write() 方法 , 进行收发消息.
- 常用方法:
- func DialTCP(net string, laddr, raddr * TCPAddr)(* TCPConn, error) net : tcp/tcp4/tcp6
- func (c * TCPConn) Close() error
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
var buf [512]byte
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:5000")
checkError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
checkError(err)
rAddr := conn.RemoteAddr()
n, err := conn.Write([]byte("Hello Server!"))
checkError(err)
n, err = conn.Read(buf[0:])
checkError(err)
fmt.Println("Reply from server: ", rAddr.String(), " #", string(buf[0:n]))
conn.Close()
}
7. UDP
- UDPAddr 地址结构体
type UDPAddr struct{
IP IP
Port int
}
- 和TCP对应 , UDP有 ResolveUDPAddr(), Network() 和String(); 原型和功能都一样.
- UDPConn对象
- UDP中C/S通过UDPConn对象连接, UDPConn是Conn接口的实现, 绑定了服务器的网络协议和地址.
- type UDPConn struct {//empty }
- UDP 不保证通信的可靠性和有序性.
- UDPConn对象提供了处理UDP数据的方法, 保证可靠
- func (c * UDPConn) ReadFromUDP(b []byte) (n int, addr * UDPAddr, err error)
- func (c * UDPConn) WriteToUDP(b []byte, addr * UDPAddr )(int, error)
- UDP Server
- 工作流程
- Server先注册一个端口号, 然后调用ListenUDP()在这个端口上穿件一个UDPConn连接对象, 并在该对象和客户机上简历不可靠连接.
- 如果服务器和某个客户机建立了UDPConn 连接, 就可以使用该对象的ReadFromUDP() 和WriteToUDP()方法.
- 不管上一次通信是否完成或正常, UDP服务器都会依然接受下一次连接请求.
- 常用方法.
- func ListenUDP(net string, laddr * UDPAddr)(* UDPAddr ,error)
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
udpAddr, err := net.ResolveUDPAddr("udp", ":5001") //使用本地地址
checkError(err)
conn, err := net.ListenUDP("udp", udpAddr)
checkError(err)
for {
handleClient(conn)
}
} func handleClient(conn *net.UDPConn) {
var buf [512]byte
n, addr, err := conn.ReadFromUDP(buf[0:])
if err != nil {
return
}
fmt.Println("Receive from client:", addr.String(), " #", string(buf[0:n]))
conn.WriteToUDP([]byte("Welcome Client!"), addr)
}
- UDP Client
- 工作流程
- 获取服务器的地址和端口号之后 , 可以调用DialUDP() 向服务器发出连接请求, 成功后返回UDPConn对象
- 客户机可以调用UDPConn对象的 ReadFromUDP() 和 WriteToUDP() 方法, 与服务器进行数据传输
- 通信结束后, Client调用Close()关闭连接.
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:5001")
checkError(err)
conn, err := net.DialUDP("udp", nil, udpAddr)
checkError(err)
_, err = conn.Write([]byte("Hello Server!"))
checkError(err)
var buf [512]byte
n, addr, err := conn.ReadFromUDP(buf[0:])
checkError(err)
fmt.Println("Reply from server:", addr.String(), " #", string(buf[0:n]))
conn.Close()
}
8. IP
- IP 是不可靠无连接的协议, 可以用IP实现自己的网络服务协议.
- type IPAddr struct{ IP IP }
- 有对应的 ResolveIPAddr() , Network(), String() 和 IPConn对象
- IP Server
- 工作流程
- IP 工作在网络层, 不需要在一个指定的端口上和客户机通信.
- IP服务器使用指定的协议簇和协议, 调用ListenIP() 创建一个IPConn连接对象
- 使用 IPConn的ReadFromIP() 和 WriteToIP() 收发数据
- 通信结束, 服务器调用Close()关闭IPConn
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
name, err := os.Hostname()
checkError(err)
ipAddr, err := net.ResolveIPAddr("ip4", name)
checkError(err)
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)
}
- IP Client
- 工作流程
- 获取服务器网址后调用DialIP() 发出连接请求 , 成功返回 IPConn对象
- 如果成功连接, 使用IPConn的ReadFromIP() 和 WriteToIP() 收发数据
- 通信结束, Client也可以调用Close() 关闭IPConn
- func DialIP(netProto string, laddr , raddr * IPAddr)(*IPConn,error) netProto : "网络类型 + 协议名" (i.e. ip4:ip or ip4:4)
package main import (
"fmt"
"net"
"os"
) func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal Error: %s", err.Error())
os.Exit(1)
}
} func main() {
lAddr, err := net.ResolveIPAddr("ip4", "127.0.0.1")
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 from server:", addr.String(), " #", string(buf[0:n]))
conn.Close()
}
Go 网络编程笔记的更多相关文章
- Linux网络编程笔记(修订版)
我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1. 基本概念 2. 基本接口 2.1. 打开一个socket 2.2. 将 ...
- Winsock网络编程笔记(1)----入门
今天第一次接触winsock网络编程,看的资料是Windows网络编程第二版.通过博客记住自己的看书笔记.. 在这里贴出第一个程序,虽然程序什么都没做,但以此作为入门,熟悉其网络编程风格.. #inc ...
- Java之网络编程笔记
网络通讯要素: 1.IP地址 IP地址:用于标记一台计算机的身份证. IP地址由网络地址(确定网络)和主机地址(网络中的主机)组成. IP地址分为A类地址.B类地址.C类地址(常用).D类地址.E类地 ...
- iOS网络编程笔记——Socket编程
一.什么是Socket通信: Socket是网络上的两个程序,通过一个双向的通信连接,实现数据的交换.这个双向连路的一端称为socket.socket通常用来实现客户方和服务方的连接.socket是T ...
- Winsock网络编程笔记(4)----基本的理论知识
前面的笔记记录了Winsock的入门编程,领略了Winsock编程的乐趣..但这并不能算是掌握了Winsock,加深理论知识的理解才会让后续学习更加得心应手..因此,这篇笔记将记录一些有关Winsoc ...
- Java基础知识强化之网络编程笔记18:Android网络通信之 使用HttpClient的Post / Get 方式读取网络数据(基于HTTP通信技术)
使用HttpClient进行Get方式通信,通过HttpClient建立网络链接,使用HttpGet方法读取数据,并且通过Response获取Entity返回值. 使用HttpClient进行Post ...
- Java基础知识强化之网络编程笔记17:Android网络通信之 使用Http的Post方式读取网络数据(基于HTTP通信技术)
使用Http的Post方式与网络交互通信.Post方式需要向网络传输一部分数据,同时具有输入流和输出流. 详见:Android(java)学习笔记210:采用post请求提交数据到服务器(qq登录案例 ...
- Java基础知识强化之网络编程笔记16:Android网络通信之 使用Http的Get方式读取网络数据(基于HTTP通信技术)
使用Http的Get方式读取网络数据,使用Get方式与网络通信是最常见的Http通信,建立链接之后就可以通过输入流读取网络数据. 详见:Android(java)学习笔记209:采用get请求提交数据 ...
- iOS网络编程笔记——Socket底层实现笔记
Socket简单底层实现笔记: 以Socket客户端编程为例: 1.导入头文件 #import <arpa/inet.h> #import <netinet/in.h> #im ...
随机推荐
- 初学java记录
记录一: if语句: if(x < y) System.out.println("x is less than y"); 记录二: 强制转换字符类型赋值的方法: num2= ...
- Java中 如何把Object类型强转成Map<String, String>类型
首先你需要保证要转换的Object的实际类型是Map<String, String> 假设Object变量名为obj,强制转换(Map<String, String>)obj ...
- 支付宝吱口令自动复制脚本,自动复制 JavaScript 代码介绍
本文转自:http://www.sojson.com/blog/262.html 最近支付宝#吱口令#的信息随处可见,可谓是铺天盖地,群里发这样的信息给被踢了不少.我开始还在鄙视这些人,有几个小钱?然 ...
- Vim编辑器基本操作学习(一)
最近在服务端编辑文件总不可避免要使用vim编辑器,下面就对学习到的常用命令进行总结,以便自己以后查看. 基本编辑命令 删除字符:x 删除一行:dd 删除换行符:J,同时将两行合并成一行 撤 ...
- grep 小技巧
转自:http://www.cnblogs.com/itech/archive/2012/10/18/2729944.html 1) grep命令加- E参数,这一扩展允许使用扩展模式匹配.例如,要抽 ...
- 016:Explain
一. Explain EXPLAIN 官方文档 1.explain说明 explain是解释SQL语句的执行计划,即显示该SQL语句怎么执行的 使用explain的时候,也可以使用desc 5.6 版 ...
- Git版本控制:Github的使用之 多人协作及参与项目
版权声明:本文为博主皮皮http://blog.csdn.net/pipisorry原创文章,未经博主允许不得转载. 目录(?)[-] Git多人协作 从远程库克隆 使用GitHub参与开源项目- ...
- 【Eclipse】开发专题
Eclipse插件安装 参考以下几个网页内容 不同版本Eclipse对JDK版本要求http://blog.csdn.net/kevin_pso/article/details/54971739 Ec ...
- 使用CCNode作为容器容易踩的坑
Cocos2dx中CCNode经常作为一个父容器,里面装一些UI控件,最后组成一个复杂的自定义的UI控件,但是在使用别人的自定义控件和自己写自定义问题的时候会踩一些坑. 首先拿到一个自定义的UI控件一 ...
- 完美解决 开机无法启动 提示0xc000000e
注:昨天装系统碰到这个问题,这个方法说的较详细,我的是WIN7系统,开机提示引导文件错误,代码为0xc000000e 无法进入系统,使用PE进入后,在运行里输入CMD,然后按下文红字开始操作 完美解决 ...