Golang之实战篇(1)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

"千篇一律高手寂寞。几十不惑全都白扯"

上篇介绍了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做了一些小的代码。

感谢你的阅读

祝你好运向阳而生~

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go