上篇介绍了golang这门新的语言的一些语法。那么我们能用golang简单地写些什么代码出来呢?
一、猜数字
这个游戏的逻辑很简单。系统随机给你生成一个数,然后读取你猜的数字,再根据你猜的数字 跟系统生成的数字比较。告诉你结果这样。
(1)随机生成一个数
随机生成一个区间在1~100之间的数。
import math/rand
import time
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNum := rand.Intn(maxNum)
fmt.Println("The secert number is:", secretNum)
}
用C\C++写过类似程序的朋友们,都会知道使用rand 的时候,如果不设置rand种子,那么生成的随机数是固定的。因此,我们import time,用time中的时间戳,设置rand的种子。
(2)获取用户输入的值
生成要猜的数字是第一步,那么接下来要进行的就是获取用户输入的值。在go中有两个解法
fmt.Scanf;
这个fmt.Scanf的使用和C中的scanf相差无几。也是最简单的获取值的方式。
bufio\os;
bufio:
Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.
bufio包实现IO缓冲。它包装了一个可读或者可写的io对象,并创建一个另一个实现接口的对象,为文本文件提供帮助和缓冲
如果有一定的linux系统的知识,对"linux下一切皆文件"这句话不陌生。在linux系统看来,我们的显示器是文件。程序获取用户的数据,是从键盘输入回显到显示器,再从显示器中拿到的。
os:
Package os provides a platform-independent interface to operating system functionality. The design is Unix-like, although the error handling is Go-like; failing calls return values of type error rather than error numbers. Often, more information is available within the error. For example, if a call that takes a file name fails, such as Open or Stat, the error will include the failing file name when printed and will be of type *PathError, which may be unpacked for more information.
简单来说,这个包是提供给用户,独立地操作系统功能的接口。
将从显示器的文件数据,创建另一个对象(reader)管理
reader提供了ReadString的API接口。我试试看
我们通过readString函数,把reader中的值给input。但是这个值不是很干净! 因为有"\r\n"。这个的存在是因为我们敲击了回车!为此,我们需要将input末尾的后缀给干掉!
我们使用strings包里面TrimSuffix的这个函数,顾名思义,是一个过滤后缀的函数。
(3)游戏逻辑判断
打印到显示器上的字符,到底是数字还是字符串?答案是字符串!我们得到的input的值,其实是一段自子串。因此,我们用到如TrimSuffix函数 是针对字符串类型的,而字符串的大小比较 和 整数的大小比较是不一样的!
strconv;
package strconv implements conversions to and from string representations of basic data types
包strconv实现基本数据类型的字符串表示形式之间的相互转换。
后面判断的逻辑也比较简单。也就不多言了。
(4)测试
package main
import (
"fmt"
"math/rand"
"time"
//系统接口
"bufio"
"os"
//strings操作
"strconv"
"strings"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNum := rand.Intn(maxNum)
fmt.Println("The secert number is:", secretNum)
for {
fmt.Println("Please Enter your guess~ ")
// input := 0
// fmt.Scanf("%d", &input)
// fmt.Println(input)
reader := bufio.NewReader(os.Stdin)
//拆解
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An input Error,Please reinput again", err)
return
}
//fmt.Printf("Befor:%#v\n", input)
input = strings.TrimSuffix(input, "\n")
input = strings.TrimSuffix(input, "\r")
//fmt.Printf("After:%#v\n", input)
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input,Please Enter your guess again~", err)
return
}
fmt.Println("Your guess is", guess)
//逻辑判断
if guess > secretNum {
fmt.Println("More bigger")
} else if guess < secretNum {
fmt.Println("Less smaller")
} else {
fmt.Println("You are preety good,and correct: ", secretNum)
break
}
}
}
二、在线词典
我们准备通过http,得到一个英文单词的翻译。
大概是这样的格式。那么制作这个肯定需要一定的网络基础。
(1)处理请求
main;
我们通过命令行,上传我们的请求。这里又要使用到我们的"老朋友" ——os package
JSON;
JSON是一种轻量级的数据交换格式。它采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
//请求报头
type DictRequest struct{
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
有了请求报头,接下来就需要应对对端返回的json数据应该如何解析。对于js/python这些脚本语言,有字典、map的概念,可以直接从json里面取值。但是对golang是不行的。需要golang创建一个结构体作为解析时的填充对象。
网上有这样结构体代码的生成工具,要我们自己写肯定很麻烦。
type DictResponse struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
这样我们就得到了JSON结构体。解析http返回的报头。
net/http;
Package http provides HTTP client and server implementations.
包http提供了HTTP客户端和服务器实现。
client是http中的一个对象
Marshal;
bytes;
Package bytes implements functions for the manipulation of byte slices. It is analogous to the facilities of the strings package.
实现了操作子串切片的操作。类似字符串包strings的功能
(2)处理响应
发送一个http请求,并返回一个http响应。
而此时我们响应数据全在resp中。我们需要的是响应数据中的正文部分。
ioutil;
Package util contains utility code for use by volume plugins.
这个包提供一些好用的工具
但是,我们此时拿到的正文数据,是JSON格式的。我们为此需要将次填入我们为JSON事先创建好的结构体中。这个过程叫做,反序列化。
我们也就拿到返回的数据内容了。
(3)数据解析
我们来看看效果吧~
三、SOCKET5代理
(1)代理服务器简介
代理服务器(Proxy Server)的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站,是个人网络和Internet服务商之间的中间代理机构,负责转发合法的网络信息,对转发进行控制和登记。
SOCKS5 是一个代理协议,它在使用TCP/IP通讯的前端机器和服务器机器之间扮演一个中介角色,使得 内部网 中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过 TCP/IP协议 进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。 SOCKS5虽然是代理服务协议,但是不用作",它的协议是明文传输。
(2)SOCKS5原理
(3)建立TCP
package main
import (
"log"
"net"
)
func main() {
server, err := net.Listen("tcp", "127.0.0.1:8801")
if err != nil {
log.Fatal(err)
}
for {
client, err := server.Accept()
if err != nil {
log.Printf("Accept faild:%v", err)
//没有链接到来持续等待即可
continue
}
//连接到来
go proceess(client)
}
}
func proceess(conn net.Conn) {
}
goroutinue 可以类比一个进程里的子进程,但是开销小很多。
Process;
func proceess(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
b, err := reader.ReadByte()
if err != nil {
break
}
_, err = conn.Write([]byte{b})
if err != nil {
break
}
}
}
defer这一行的含义,就是在函数结束时,释放掉资源。防止资源泄漏
接下来我们使用的bufio。我们在猜数字的时候用过。它为可读或可写的对象提供一个缓冲区。这里的IO输出是针对网络套接字,但是IO的速度是很慢的。因此,我们把网络数据 转换为bufio的形式,减少底层系统调用,同时,bufio也会提供一定的工具帮我们对数据进行获取。
这里conn底层回去调用Write。conn保存了系统为这个连接打开的fd。
(4)认证阶段
代理服务器不仅仅是获取数据即可。否则我们刚刚完成一个TCP连接即可。
认证时,浏览器会发三个字段给代理服务器:version、methods、methods编码
version;
method;
它返回复制的字节数,如果读取的字节数较少,则返回一个错误。
这样我们也就获取了version与method;此时,代理服务器需要向 浏览器返回两个字段,version\method;
(5)请求阶段
在这个阶段,我们就需要拿到浏览器url里面访问的IP+PORT,定位服务器。此时我们增加了一个connect函数帮我们完成这个功能
atyp地址类别;
如果是IPV4,我们逐个将这刚好4字节打印成IP地址的格式。
如果是host,我们则需要重新计算大小,转换成字符串并保存。
IPV6这里算了。用得少。
端口port;
我们日常生活中的有大端机,小端机。所谓大小端机最大的差别,就在于存储字符的字节序不同。为了屏蔽掉这个差异,网络中统一发送的字节序为大端字节序。如果是小端机接收,那么你就需要进行转换,如果是大端,那就什么都可以不做。
(6)Relay阶段
此时两端主机正式开始建立连接了。
但是有个问题是,一旦执行到这里,connect函数就会结束。那么两个端的连接也就会被关闭。因此,什么时候应该是将两端连接关闭呢? 一旦其中一方出现copy错误时~
context;
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
包上下文定义了上下文类型,它携带截止日期、取消信号和其他跨API边界和进程间的请求范围的值。
当调用cancel()的时候, 处于等待的ctx.Done()会立即返回。
以上也就简简单单用golang做了一些小的代码。