"千篇一律,高手寂寞。几十不惑,全都白扯"
上篇介绍了golang这门新的语言的一些语法。那么我们能用golang简单地写些什么代码出来呢?
这个游戏的逻辑很简单。系统随机给你生成一个数,然后读取你猜的数字,再根据你猜的数字 跟系统生成的数字比较。告诉你结果这样。
随机生成一个区间在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的种子。
生成要猜的数字是第一步,那么接下来要进行的就是获取用户输入的值。在go中有两个解法
这个fmt.Scanf的使用和C中的scanf相差无几。也是最简单的获取值的方式。
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的这个函数,顾名思义,是一个过滤后缀的函数。
打印到显示器上的字符,到底是数字还是字符串?答案是字符串!我们得到的input的值,其实是一段自子串。因此,我们用到如TrimSuffix函数 是针对字符串类型的,而字符串的大小比较 和 整数的大小比较是不一样的!
package strconv implements conversions to and from string representations of basic data types
包strconv实现基本数据类型的字符串表示形式之间的相互转换。
后面判断的逻辑也比较简单。也就不多言了。
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,得到一个英文单词的翻译。
大概是这样的格式。那么制作这个肯定需要一定的网络基础。
我们通过命令行,上传我们的请求。这里又要使用到我们的"老朋友" ——os package
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返回的报头。
Package http provides HTTP client and server implementations.
包http提供了HTTP客户端和服务器实现。
client是http中的一个对象
Marshal;
Package bytes implements functions for the manipulation of byte slices. It is analogous to the facilities of the strings package.
实现了操作子串切片的操作。类似字符串包strings的功能
发送一个http请求,并返回一个http响应。
而此时我们响应数据全在resp中。我们需要的是响应数据中的正文部分。
Package util contains utility code for use by volume plugins.
这个包提供一些好用的工具
但是,我们此时拿到的正文数据,是JSON格式的。我们为此需要将次填入我们为JSON事先创建好的结构体中。这个过程叫做,反序列化。
我们也就拿到返回的数据内容了。
我们来看看效果吧~
代理服务器(Proxy Server)的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站,是个人网络和Internet服务商之间的中间代理机构,负责转发合法的网络信息,对转发进行控制和登记。
取自这里
SOCKS5 是一个代理协议,它在使用TCP/IP通讯的前端机器和服务器机器之间扮演一个中介角色,使得 内部网 中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过 TCP/IP协议 进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。
SOCKS5虽然是代理服务协议,但是不用作"",它的协议是明文传输。
取自这里
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 可以类比一个进程里的子进程,但是开销小很多。
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。
代理服务器不仅仅是获取数据即可。否则我们刚刚完成一个TCP连接即可。
认证时,浏览器会发三个字段给代理服务器:version、methods、methods编码
它返回复制的字节数,如果读取的字节数较少,则返回一个错误。
这样我们也就获取了version与method;此时,代理服务器需要向 浏览器返回两个字段,version\method;
在这个阶段,我们就需要拿到浏览器url里面访问的IP+PORT,定位服务器。此时我们增加了一个connect函数帮我们完成这个功能
如果是IPV4,我们逐个将这刚好4字节打印成IP地址的格式。
如果是host,我们则需要重新计算大小,转换成字符串并保存。
IPV6这里算了。用得少。
我们日常生活中的有大端机,小端机。所谓大小端机最大的差别,就在于存储字符的字节序不同。为了屏蔽掉这个差异,网络中统一发送的字节序为大端字节序。如果是小端机接收,那么你就需要进行转换,如果是大端,那就什么都可以不做。
此时两端主机正式开始建立连接了。
但是有个问题是,一旦执行到这里,connect函数就会结束。那么两个端的连接也就会被关闭。因此,什么时候应该是将两端连接关闭呢? 一旦其中一方出现copy错误时~
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做了一些小的代码。
感谢你的阅读,
祝你好运,向阳而生~