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 是動態埠
? 這些埠,程式員可以使用
埠-使用注意
-
在計算機(尤其是服務器)要盡可能少開埠
-
一個埠只能被一個程式監聽
-
如果使用netstat -an 可以查看本機有哪些埠在監聽
-
可以使用netstat -anb 來查看監聽埠的pid,再結合任務管理器關閉不安全的埠
tcp socket編程的客戶端和服務端
tcp socket 編程,簡稱socket編程,下圖為Go socket編程中客戶端和服務器端的網路分布

tcp socket編程的快速入門
服務端的處理流程
-
監聽埠8888
-
接收客戶端的tcp鏈接,建立客戶端和服務器端的鏈接
-
創建goroutine,處理該鏈接的請求(通常客戶端會通過鏈接發送請求包)
客戶端的處理流程
-
建立與服務端的鏈接
-
發送請求資料[終端],接收服務器端回傳的結果資料
-
關閉鏈接
簡單的程式示意圖

代碼實作
服務器端功能:
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 發送資訊
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/27361.html
標籤:Go
下一篇:go語言系列-redis
