go語言系列-TCP編程


TCP編程

Go的主要設計目標之一就是面向大規模后端服務程序,網絡通信這塊是服務端,程序必不可少也是至關重要的一部分

網絡編程基本介紹

網絡編程有兩種

TCP socket編程,是網絡編程的主流。之所以叫Tcp socket 編程,是因為底層基於Tcp/ip協議的。比如:QQ聊天

b/s結構的http編程,使用瀏覽器去訪問服務器時,使用的就是http協議,而http底層依舊是用tcp socket實現的。比如:京東商城【屬於go web開發范疇】

計算機間要相互通訊,必須要是用網線、網卡或者無線網卡

協議(tcp/ip)

TCP/IP(Transmission Control Protocol/Internet Protocol)的簡寫,中文譯名為傳輸控制協議/因特網互聯協議,又叫網絡通訊協議,這個協議是Internet最基本的協議、Internet國際互聯網絡的基礎,簡單地說,就是由網絡層的IP協議和傳輸層的TCP協議組成的

OSI與TCP/IP參考模型

書籍推薦《tcp/ip協議3卷》

IP地址

每個internet上的主機和路由器都有一個ip地址,它包括網絡號和主機號,ip地址有ipv4(32位)或者ipv6(128位)可以ipconfig查看

端口(port)-介紹

這里所指的端口不是指物理意義上的端口,而是特指TCP/IP協議中的端口,是邏輯意義上的端口

如果把IP地址比作一間房子,端口就是出入這間房子的門。真正的房子只有幾個門,但是IP地址的端口可以有65536(即:256 × 256)個之多!端口是通過端口號來標記的,端口號只有整數,范圍是從0到65535(256 × 256 - 1)

端口-分類

0號是保留端口

1 - 1024 是固定端口(程序員不要使用

​ 又叫有名端口,即被某些程序固定使用,一般程序員不使用

​ 22:SSH遠程登陸協議 23:telnet使用 21:ftp使用 25:smtp使用 80:iis使用 7:echo 使用

1025 - 65535 是動態端口

​ 這些端口,程序員可以使用

端口-使用注意

  1. 在計算機(尤其是服務器)要盡可能少開端口

  2. 一個端口只能被一個程序監聽

  3. 如果使用netstat -an 可以查看本機有哪些端口在監聽

  4. 可以使用netstat -anb 來查看監聽端口的pid,再結合任務管理器關閉不安全的端口

tcp socket編程的客戶端和服務端

tcp socket 編程,簡稱socket編程,下圖為Go socket編程中客戶端和服務器端的網絡分布

tcp socket編程的快速入門

服務端的處理流程

  1. 監聽端口8888

  2. 接收客戶端的tcp鏈接,建立客戶端和服務器端的鏈接

  3. 創建goroutine,處理該鏈接的請求(通常客戶端會通過鏈接發送請求包)

客戶端的處理流程

  1. 建立與服務端的鏈接

  2. 發送請求數據[終端],接收服務器端返回的結果數據

  3. 關閉鏈接

簡單的程序示意圖

代碼實現

服務器端功能

1.編寫一個服務器端程序,在8888端口監聽

2.可以和多個客戶端創造鏈接

3.鏈接成功后,客戶端可以發送數據,服務器端接受數據,並顯示在終端上

4.先使用telnet來測試,然后編寫客戶端程序來測試

import (
   "fmt"
   "net" //做網絡socket開發時,net包含有我們需要所有的方法和函數
   _"io"
)

func process(conn net.Conn)  {
   //這里我們循環的接受客戶端發送的數據
   defer conn.Close() //關閉conn
   for {
      //創建一個新的切片
      buf := make([]byte,1024)
      //conn.Read(buf)
      //1. 等待客戶端通過conn發送信息
      //2. 如果客戶端沒有write[發送],那么協程就阻塞在這里
      fmt.Printf("服務器在等待客戶端%s 發送信息\n", conn.RemoteAddr().String())
      n, err := conn.Read(buf) //從conn讀取
      if err != nil {
         fmt.Printf("客戶端退出 err = %v", err)
         return // !!!
      }
      //3. 顯示客戶端發送的內容到服務器的終端
      fmt.Print(string(buf[:n]))
   }
}
func main()  {
   fmt.Println("服務器開始監聽...")
   //net.Listen("tcp", "0.0.0.0:8888")
   //1. tcp 表示使用網絡協議是tcp
   //2. 0.0.0.0:8888 表示在本地監聽 8888端口
   listen, err := net.Listen("tcp","0.0.0.0:8888")
   if err != nil {
      fmt.Println("listen err = ", err)
      return
   }
   defer listen.Close() // 延時關閉listen
   //循環等待客戶端來鏈接我
   for {
      //等待客戶端鏈接
      fmt.Println("等待客戶端來鏈接...")
      conn, err := listen.Accept()
      if err != nil {
         fmt.Println("Accept() err =", err)
      } else {
         fmt.Printf("Accept() suc con = %v 客戶端 ip = %v\n", conn, conn.RemoteAddr().String())
      }
      //這里准備其一個協程,為客戶端服務
      go process(conn)
   }
   //fmt.Printf("liaten suc = %v\n",listen)
}

客戶端功能:

1.編寫一個客戶端端程序,能鏈接到服務器端的8888端口

2.客戶端可以發送單行數據,然后就退出

3.能通過終端輸入數據(輸入一行發送一行),並發送給服務器端

4.在終端輸入exit,表示退出程序

import (
   "bufio"
   "fmt"
   "net"
   "os"
)

func main()  {
   conn, err := net.Dial("tcp", "0.0.0.0:8888")
   if err != nil {
      fmt.Println("client dial err = ", err)
      return
   }
   //功能一:客戶端可以發送單行數據,然后就退出
   reader := bufio.NewReader(os.Stdin) //os.Stdin 代表標准輸入[終端]
   //從終端讀取一行用戶輸入,並准備發送給服務器
   line, err := reader.ReadString('\n')
   if err != nil {
      fmt.Println("readString err = ", err)
   }
   //再將line發送給服務器
   n, err := conn.Write([]byte(line))
   if err != nil {
      fmt.Println("conn.Write err =", err)
   }
   fmt.Printf("客戶端發送了 %d 字節的數據,並退出", n)
}

驗證 先執行服務端程序 再執行客戶端程序

服務器開始監聽...
等待客戶端來鏈接...
Accept() suc con = &{{0xc00007e2c0}} 客戶端 ip = 127.0.0.1:65393
等待客戶端來鏈接...
服務器在等待客戶端127.0.0.1:65393 發送信息

對客戶端進行改進

import (
   "bufio"
   "fmt"
   "net"
   "os"
   "strings"
)

func main()  {
   conn, err := net.Dial("tcp", "0.0.0.0:8888")
   if err != nil {
      fmt.Println("client dial err = ", err)
      return
   }
   //功能一:客戶端可以發送單行數據,然后就退出
   reader := bufio.NewReader(os.Stdin) //os.Stdin 代表標准輸入[終端]
   for {
      //從終端讀取一行用戶輸入,並准備發送給服務器
      line, err := reader.ReadString('\n')
      if err != nil {
         fmt.Println("readString err = ", err)
      }
      //如果用戶輸入的是exit就退出
      line = strings.Trim(line, " \r\n")
      if line == "exit" {
         fmt.Println("客戶端退出...")
         break
      }

      //再將line發送給服務器
      _, err = conn.Write([]byte(line + "\n"))
      if err != nil {
         fmt.Println("conn.Write err =", err)
      }
   }
   //fmt.Printf("客戶端發送了 %d 字節的數據,並退出", n)
}

驗證

hello zisefeizhu

服務器開始監聽...
等待客戶端來鏈接...
Accept() suc con = &{{0xc0000902c0}} 客戶端 ip = 127.0.0.1:49248
等待客戶端來鏈接...
服務器在等待客戶端127.0.0.1:49248 發送信息
hello zisefeizhu
服務器在等待客戶端127.0.0.1:49248 發送信息


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM