1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Go语言学习笔记 (网络编程)

Go语言学习笔记 (网络编程)

时间:2019-03-02 00:32:45

相关推荐

Go语言学习笔记  (网络编程)

网络编程分层概念:​

层与协议

简单的例子:

package mainimport ("fmt""net")func main() {listenner, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Listen err=", err)return}defer listenner.Close()//阻塞等待用户的连接conn, err1 := listenner.Accept()if err1 != nil {fmt.Println("listenner.Accept err1=", err1)return}defer conn.Close()//接收客户端的数据buf := make([]byte, 1024)n, err2 := conn.Read(buf)if n == 0 {fmt.Println("Read err=", err2)return}fmt.Printf("#%v#", string(buf[:n]))}

TCP 服务器

package mainimport ("fmt""net" //网络操作需要net包)func main() {//运行客户端go clien()//监听listener, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("err=", err)return}defer listener.Close()//阻塞等待用户链接conn, err := listener.Accept()if err != nil {fmt.Println("err=", err)return}//接收用户的请求buf := make([]byte, 1024)n, err1 := conn.Read(buf)if err1 != nil {fmt.Println("err=", err)return}fmt.Println("服务器端收到的信息:buf=", string(buf[:n]))defer conn.Close() //关闭当前用户连接}// netchat 参数nc 127.0.0.1 8000// 之后打入内容回车即可

// TCP客户端

// 客户端package mainimport ("fmt""net""os")//多任务,读键盘内容,读服务器回复内容func main() {//主动连接服务器conn, err := net.Dial("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("客户端:err=", err)return}//main调用完毕,关闭客户端连接defer conn.Close()go func() { //子协程:从键盘输入内容,给服务器发送内容str := make([]byte, 1024)for {//从键盘读取内容,放在strn, err := os.Stdin.Read(str)if err != nil {fmt.Println("客户端:os.Stdin.Read err", err)return}//把输入的内容给服务器发送conn.Write(str[:n])}}()//主协程,接收服务器回复的数据//切片缓存buf := make([]byte, 1024)for {//接收服务器的请求n, err := conn.Read(buf)if err != nil {fmt.Println("conn.Read err", err)return}//打印接收到的内容addr := conn.RemoteAddr().String()fmt.Printf("[%s]服务端回复数据: %s", addr, string(buf[:n]))}}

简单版并发服务器模型

服务端接收到客户端的数据后,将数据中所有小写字母处理为大写,再发送回客户端

package mainimport ("fmt""net" //网络操作需要net包"strings")//处理用户请求func HandleConn(conn net.Conn) { //conn属于接口类型//函数调用完毕自动关闭conndefer conn.Close()//获取客户端的网络地址信息addr := conn.RemoteAddr().String()fmt.Printf("客户端:[%s] 连接成功\n\n", addr)for {//读取用户数据buf := make([]byte, 2048)n, err := conn.Read(buf)if err != nil {fmt.Println("err=", err)return}fmt.Printf("[%s]\n客户端发送数据: %s\n\n", addr, string(buf[:n]))//通过nc命令发送过来的数据都带有换行,所以n-1 和在字符串"exit"后加"\n"就是为了杜绝此问题if "exit" == string(buf[:n-2]) || "exit\r\n" == string(buf[:n]) || "exit" == string(buf[:n-1]) || "exit\n" == string(buf[:n]) || "exit" == string(buf[:n]) {fmt.Printf("客户端:[%s]下线了\n", addr)return}//把数据转换为大写,再给用户发送conn.Write([]byte(strings.ToUpper(string(buf[:n]))))} //防止处理一次就断开}func main() {//运行客户端go clien()//监听listener, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("err=", err)return}defer listener.Close()//接收多个用户for {conn, err := listener.Accept()if err != nil {fmt.Println("err=", err)return}//处理用户请求,每来一个用户就新建一个协程go HandleConn(conn)}}func clien() {//主动连接服务器conn, err := net.Dial("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("err=", err)return}defer conn.Close()//发送数据conn.Write([]byte("exit"))buf := make([]byte, 1024)n, err1 := conn.Read(buf)if err1 != nil {fmt.Println("err=", err)return}addr := conn.RemoteAddr().String()fmt.Printf("[%s]\n服务端回复数据:%s\n", addr, string(buf[:n]))}

文件的传输

1.文件的发送

//send.go// 发文件//获取文件属性package mainimport ("fmt""io""net""os")//发送文件内容func SendFile(path string, conn net.Conn) {//以只读方式打开文件f, err := os.Open(path)if err != nil {fmt.Println("os.Open err=", err)return}defer conn.Close()defer f.Close()//读文件内容,读多少,发多少buf := make([]byte, 1024*4)for {n, err := f.Read(buf) //从文件读取内容if err != nil {if err == io.EOF {fmt.Println("文件发送完毕")} else {fmt.Println("f.Read err=", err)}return}//发送内容conn.Write(buf[:n]) //给服务器发送内容}}func main() {//提示输入文件fmt.Println("请输入需要传输的文件:")var path stringfmt.Scan(&path)// list := os.Args// if len(list) != 2 {// fmt.Println("Useage: xxx file")// return// }//获取文件名//fileName := list[1]//获取文件名字 info.Name()info, err := os.Stat(path)fmt.Println("name=", info.Name())fmt.Println("size=", info.Size())if err != nil {fmt.Println("err=", err)return}//主动连接服务器conn, err1 := net.Dial("tcp", "127.0.0.1:8000")if err1 != nil {fmt.Println("net.Dial err1=", err1)return}defer conn.Close()//给接收方,先发送文件名_, err = conn.Write([]byte(info.Name()))if err != nil {fmt.Println("coon.Write err=", err)return}//接收对方的回复,如果回复ok,说明对方准备好了,可以发文件var n intbuf := make([]byte, 1024)n, err3 := conn.Read(buf)if err3 != nil {fmt.Println("conn.Read err3=", err3)return}if "ok" == string(buf[:n]) {//发送文件SendFile(path, conn)}}

2.文件接收

// 收文件//recv.gopackage mainimport ("fmt""io""net""os")func RecvFile(fileName string, conn net.Conn) {f, err := os.Create(fileName)if err != nil {fmt.Println("os.Create err=", err)return}buf := make([]byte, 1024*4)//接收多少写多少,一点不差for {n, err1 := conn.Read(buf) //接收对方发过来的文件内容if err1 != nil {if err1 == io.EOF { //接收完成fmt.Println("文件接收完毕")//return} else {fmt.Println("conn.Read err1=", err1)}return}if n == 0 {fmt.Println("文件接收完毕")return}f.Write(buf[:n]) //往文件写入内容}}func main() {//监听Listenner, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Listen err", err)return}defer Listenner.Close()//阻塞等待用户连接conn, err1 := Listenner.Accept()if err1 != nil {fmt.Println("Listener.Accept err1=", err1)return}defer conn.Close()buf := make([]byte, 1024)n, err2 := conn.Read(buf)if err2 != nil {fmt.Println("conn.Read err2=", err2)return}fileName := string(buf[:n]) //读取对方发送的文件名conn.Write([]byte("ok")) //给对方回复okRecvFile(fileName, conn) //接收文件内容}

实现群聊的效果

//并发聊天服务器.gopackage mainimport ("fmt""net""strings""time")type Client struct {C chan string //用于发送数据的管道Name string//用户名Addr string//网络地址}//保存在线用户,cliAddr====》Clientvar onlineMap map[string]Clientvar message = make(chan string)func Manager() {//给map分配空间onlineMap = make(map[string]Client)for {msg := <-message //没有消息前,这里会阻塞//遍历map,给map每个成员发消息for _, cli := range onlineMap {cli.C <- msg}}}func WriteMsgToClient(cli Client, conn net.Conn) {for msg := range cli.C {//给当前客户端发送信息conn.Write([]byte(msg + "\n"))}}func MakeMsg(cli Client, msg string) (buf string) {buf = "[" + cli.Addr + "]" + cli.Name + ": " + msgreturn}func HandleConn(conn net.Conn) {defer conn.Close()//获取客户端的网络地址cliAddr := conn.RemoteAddr().String()//创建一个结构体cli := Client{make(chan string), cliAddr, cliAddr}//把结构体添加到maponlineMap[cliAddr] = cli//新开一个协程,专门给当前客户端发送信息go WriteMsgToClient(cli, conn)//广播某个人在线//message<-"["+cli.Addr+"]"+cli.Name+": login"message <- MakeMsg(cli, "login")//提示,我是谁cli.C <- MakeMsg(cli, "I am here")isQuit := make(chan bool) //对方是否主动退出hasData := make(chan bool) //对方是否有数据发送//新开一个协程接收用户发送过来的数据go func() {for {buf := make([]byte, 1024*2)n, err := conn.Read(buf)if n == 0 { //对方断开,或者出问题isQuit <- truefmt.Println("conn.Read err=", err)return}msg := string(buf[:n-1]) //通过windows nc测试,多一个换行if len(msg) == 3 && msg == "who" {//遍历map,给当前用户发送所有成员conn.Write([]byte("user list:\n"))for _, tmp := range onlineMap {msg := tmp.Addr + ":" + tmp.Name + "\n"conn.Write([]byte(msg))}} else if len(msg) >= 8 && msg[:6] == "rename" {//raname|mikename := strings.Split(msg, "|")[1] //要第一个cli.Name = nameonlineMap[cliAddr] = cliconn.Write([]byte("rename success !!!\n"))} else {//转发此内容message <- MakeMsg(cli, msg)}hasData <- true //代表有数据}}()for {//通过select检测channel的流动select {case <-isQuit:delete(onlineMap, cli.Addr)//当前用户从map移除message <- MakeMsg(cli, "login out") //广播谁下线了case <-hasData:case <-time.After(30 * time.Second): //60秒后超时了delete(onlineMap, cliAddr)message <- MakeMsg(cli, "time out leave out")return}}}func main() {//监听listenner, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Listen err=", err)return}defer listenner.Close()//新开一个协程,转发消息,只要有消息来了,就遍历map给map每一个成员都发送消息go Manager()//主协程,循环阻塞等待用户连接for {conn, err := listenner.Accept()if err != nil {fmt.Println("listenner.Accept err=", err)continue}go HandleConn(conn) //处理用户连接}}

一张图解决HTTP编程

客户端发送请求包,包体等略过,太繁琐

package mainimport("fmt""net")func main(){//主动连接服务器conn,err:=net.Dial("tcp",":8000")if err!=nil{fmt.Println("dial err=",err)return}defer conn.Close()requestBuf:="// ....."//先发请求包,服务器才会回响应包conn.Write([]byte(requestBuf))//接收服务器回复的响应包n,err1:=conn.Read(buf)if n==0 {/*fmt.Println("Read err=1",err1)return}fmt.Println("#%v#")}

服务端解析请求包

package mainimport ("fmt""io""log""net/http")//w,给客户端回复数据//req,读取客户端发送的数据func HelloConn(w http.ResponseWriter, req *http.Request) {fmt.Println(req.Method) //获取用户请求的方法fmt.Println(req.URL) //获取用户请求的地址fmt.Println(req.Header) //获取用户请求的包头fmt.Println(req.Body) //获取用户请求的方法//包体是空的io.WriteString(w, "hello go!\n") //给客户端回复数据}func main() {//注册处理函数,用户连接,自动调用指定函数http.HandleFunc("/go", HelloConn)//第一个参数时网址/go/mike.html,什么都不写,只写/,客户端就不用写具体哪个地址//监听绑定err := http.ListenAndServe(":8000", nil)if err != nil {log.Fatal("ListenAndServe: ", err)}}

//读取一些的信息(请求包)package mainimport ("fmt""net/http")func main() {resp, err := http.Get("") //必须写http://if err != nil {fmt.Println("http.Get err=", err)return}defer resp.Body.Close()fmt.Println("Status= ", resp.Status)fmt.Println("StatusCode= ", resp.StatusCode)fmt.Println("Header= ", resp.Header)//fmt.Println("Body= ", resp.Body)buf := make([]byte, 4*1024)var tmp stringfor {n, err := resp.Body.Read(buf)if n == 0 {fmt.Println("read err=", err)break}tmp += string(buf[:n])}fmt.Println(tmp)}

并发编程

百度贴爬虫例子

package mainimport ("fmt""net/http""regexp""os""strconv")爬取网页内容func HttpGet(url string) (result string, err error) {resp, err1 := http.Get(url)if err1 != nil {err = err1return}defer resp.Body.Close()//读取网页Boday内容buf := make([]byte, 1024*4)for {n, err := resp.Body.Read(buf)if n == 0 { //读取结束,或者出问题fmt.Println("resp.Body err=", err)break}result += string(buf[:n])}return}func SpiderPage(i int, page chan<- int) {url := "/f?kw=%E7%BB%9D%E5%9C%B0%E6%B1%82%E7%94%9F&ie=utf-8&pn=" + strconv.Itoa((i-1)*50)fmt.Printf("正在爬%d个网页%s", i, url)//2)爬(将所有的网站的内容全部爬下来)result, err := HttpGet(url)if err != nil {fmt.Println("HttpGet err=", err)return}//把内容写入到文件fileName := strconv.Itoa(i) + ".html"f, err1 := os.Create(fileName)if err1 != nil {fmt.Println("os.Create errr=", err1)return}f.WriteString(result)f.Close() //关闭当前文件page <- i}//爬取一个网页func DoWork(start, end int) {fmt.Printf("正在爬取%d 到 %d 的页面", start, end)page := make(chan int)//明确目标,(要知道你准备在哪个范围或者网站去搜索)for i := start; i <= end; i++ {go SpiderPage(i, page)}for i := start; i < end; i++ {fmt.Printf("第%d个页面爬取完成", <-page)}}func main() {var start, end intfmt.Printf("请输入起始页( >=1):")fmt.Scan(&start)fmt.Printf("请输入终止页(>=起始页):")fmt.Scan(&end)DoWork(start, end)}

捧腹段子爬虫例子

package mainimport ("fmt""net/http""os""regexp""strconv""strings")func HttpGet(url string) (result string, err error) {resp, err1 := http.Get(url) //发送get请求if err != nil {err = err1return}defer resp.Body.Close()//读取网页内容buf := make([]byte, 1024*4)for {n, _ := resp.Body.Read(buf)if n == 0 {break}result += string(buf[:n]) //累加读取内容}return}//开始爬取每一个段子func SpiderOneJoy(url string) (title, content string, err error) {//开始爬取段子信息result, err1 := HttpGet(url)if err1 != nil {//fmt.Println("HttpGet err = ", err1)err = err1return}//取关键信息//取标题,标题以<h1>开头,以</h1>结尾,re1 := regexp.MustCompile(`<h1>(?s:(.*?))</h1>`)if re1 == nil {//fmt.Println("regexp.MustCompile err ")err = fmt.Errorf("%s", "regexp.MustCompile err")return}//取内容tmpTitle := re1.FindAllStringSubmatch(result, 1) //只过滤第一个for _, data := range tmpTitle {title = data[1]//title = strings.Replace(title, "\r", "", -1)//title = strings.Replace(title, "\n", "", -1)//title = strings.Replace(title, " ", "", -1)title = strings.Replace(title, "\t ", "", -1) //剔除干扰字符换成空字符break}//取内容,内容以<div class="content-txt pt10">开头,以<a id="prev" href="结尾re2 := regexp.MustCompile(`<div class="content-txt pt10">(?s:(.*?))<a id="prev" href="`)if re2 == nil {//fmt.Println("regexp.MustCompile err ")err = fmt.Errorf("%s", "regexp.MustCompile err")return}//取内容tmpContent := re2.FindAllStringSubmatch(result, -1)for _, data := range tmpContent {content = data[1]content = strings.Replace(content, "\t", "", -1)//去掉多余信息的测试content = strings.Replace(content, "\n", "", -1)content = strings.Replace(content, "\r", "", -1)content = strings.Replace(content, "<br />", "", -1)break}return}//把内容写入到文件func StoreJoyToFile(i int, fileTitle []string, fileContent []string) {//新建文件f, err := os.Create(strconv.Itoa(i) + ".txt")if err != nil {fmt.Println("os.Create err = ", err)return}defer f.Close()//写内容n := len(fileTitle)for i := 0; i < n; i++ {f.WriteString(fileTitle[i] + "\n")//写标题f.WriteString(fileContent[i] + "\n")//写内容f.WriteString("\n-----------------------------------\n")}}func SpiderPape(i int, page chan int) {//明确需要爬取的url///xiaohua_1.htmlurl := "/xiaohua_" + strconv.Itoa(i) + ".html"fmt.Printf("正在爬取网页%d:%s\n", i, url)//开始爬取主页的链接result, err := HttpGet(url)if err != nil {fmt.Println("HttpGet err = ", err)return}//fmt.Println(result)//取url链接-正则表达式,以<h1 class="dp-b"><a href="开头,以"结尾//解释表达式re := regexp.MustCompile(`<h1 class="dp-b"><a href="(?s:(.*?))"`)if re == nil {fmt.Println("regexp.MustCompile err ")return}//取关键信息joyUrls := re.FindAllStringSubmatch(result, -1) //过滤全部//fmt.Println(joyUrls)fileTitle := make([]string, 0)fileContent := make([]string, 0)//取网址//第一个返回下标,第二个返回内容for _, data := range joyUrls {//fmt.Println(data[1])//开始爬取每一个段子title, content, err := SpiderOneJoy(data[1])if err != nil {fmt.Println("SpiderOneJoy err = ", err)continue}//fmt.Printf("title = #%v#", title)//fmt.Printf("content = #%v#", content)fileTitle = append(fileTitle, title) //追加内容fileContent = append(fileContent, content) //追加内容}//fmt.Println("fileTitle = ", fileTitle)//fmt.Println("fileContent = ", fileContent)//把内容写入到文件StoreJoyToFile(i, fileTitle, fileContent)page <- i //向管道内写入内容num}func DoWork(start, end int) {fmt.Printf("正在爬取 %d 到 %d 页面网址\n", start, end)page := make(chan int)for i := start; i <= end; i++ {//定义函数爬主页面go SpiderPape(i, page)}for i := start; i <= end; i++ {fmt.Printf("%d页面爬取完成\n", <-page)}}func main() {var start, end intfmt.Println("请输入起始页(>=1)")fmt.Scan(&start)fmt.Println("请输入起始页(>=起始页)")fmt.Scan(&end)DoWork(start, end) }

(本笔记内容整理自网络资源,侵删)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。