網(wǎng)站首頁 編程語言 正文
前言
TCP協(xié)議為傳輸控制協(xié)議,TCP協(xié)議有以下幾個(gè)特點(diǎn):
1. TCP是面向連接的傳輸層協(xié)議;
2. 每條TCP連接只能有兩個(gè)端點(diǎn),每條TCP連接是點(diǎn)到點(diǎn)的通信;
3. TCP提供可靠的交付服務(wù),保證傳送的數(shù)據(jù)無差錯(cuò),不丟失,不重要且有序;
4. TCP提供全雙工通信,允許雙方在任何時(shí)候都能發(fā)送數(shù)據(jù),為此TCP連接的兩端都設(shè)有發(fā)送緩存和接收緩存,用來臨時(shí)存放雙向通信的數(shù)據(jù);
5. TCP是面向字節(jié)流的;
發(fā)送緩存用來暫存以下數(shù)據(jù):
① 發(fā)送應(yīng)用程序傳送給發(fā)送方TCP準(zhǔn)備發(fā)送的數(shù)據(jù);
② TCP已發(fā)送但尚未收到確認(rèn)的數(shù)據(jù);
接收緩存用來暫存以下數(shù)據(jù):
① 按序到達(dá)但尚未被接收應(yīng)用程序讀取的數(shù)據(jù);
② 不按序到達(dá)的數(shù)據(jù);
因?yàn)槭敲嫦蜻B接的協(xié)議,數(shù)據(jù)像水流一樣傳輸,會(huì)存在黏包問題。
TCP連接
一個(gè)TCP服務(wù)端可以同時(shí)連接很多個(gè)客戶端,Go語言可以使用go關(guān)鍵字開啟goroutine,每建立一個(gè)連接就創(chuàng)建一個(gè)goroutine,這樣可以并發(fā)執(zhí)行每一個(gè)創(chuàng)建的連接,tcp服務(wù)端主要的處理流程有:1. 監(jiān)聽端口;2. 接收客戶端請(qǐng)求創(chuàng)建tcp連接;3. 使用go關(guān)鍵字開啟goroutine處理每一個(gè)建立的連接,收發(fā)數(shù)據(jù);4. 關(guān)閉連接;tcp客戶端主要的處理流程有:1. 建立與服務(wù)端的連接;2. 收發(fā)數(shù)據(jù);3. 關(guān)閉連接;關(guān)于tcp通信我們一般會(huì)使用到bufio,net,strings,os包,其中bufio包主要用來做輸入輸出數(shù)據(jù)的緩存,bufio.NewReader()函數(shù)可以傳遞os.Stdin類型(任何實(shí)現(xiàn)了io.Reader接口中的read()方法都可以作為參數(shù)傳遞進(jìn)來,通常是傳遞一個(gè)實(shí)現(xiàn)了io.Reader接口中的read()方法的結(jié)構(gòu)體或者是結(jié)構(gòu)體指針),os.Stdin為標(biāo)準(zhǔn)輸入,我們可以接受讀取從控制臺(tái)輸入的數(shù)據(jù),os.NewReader()函數(shù)返回的是一個(gè)新的帶有4096 byte大小緩沖區(qū)的Reader結(jié)構(gòu)體指針類型,通過Reader結(jié)構(gòu)體指針類型我們就可以調(diào)用很多的方法,比如可以調(diào)用ReadString(delim byte) (string,error)方法,ReadString方法可以一直從標(biāo)準(zhǔn)輸入中讀取數(shù)據(jù),直到遇到指定的終止符號(hào)delim,并且讀取的內(nèi)容會(huì)包含當(dāng)前的delim,所以bufio包主要用來對(duì)輸入和輸出的數(shù)據(jù)進(jìn)行緩沖;net包主要是連接的監(jiān)聽、創(chuàng)建,以及連接數(shù)據(jù)的讀取和寫入(例如tcp,udp等網(wǎng)絡(luò)編程)等相關(guān)工作,tcp服務(wù)端中可以使用一個(gè)process()函數(shù)傳遞一個(gè)net.Conn類型也即*net.TCPConn類型的變量conn(TCPConn結(jié)構(gòu)體實(shí)現(xiàn)了Conn接口),*TCPConn類型那么就可以調(diào)用很多的方法,因?yàn)槲覀冃枰獜膖cp連接中讀取客戶端或者是服務(wù)端發(fā)送的數(shù)據(jù),所以可以調(diào)用*TCPConn類型的read()方法讀取tcp連接的數(shù)據(jù),然后輸出數(shù)據(jù)即可,服務(wù)端可以使用net.Listen()函數(shù)監(jiān)聽連接,使用Accept()方法建立tcp連接,客戶端則可以使用net.Dial()連接創(chuàng)建的tcp連接,使用conn接口的實(shí)現(xiàn)*TCPConn(Dial()方法的返回值就是*TCPConn類型)調(diào)用conn的read()方法將讀取tcp連接的內(nèi)容,write()方法將數(shù)據(jù)寫入到tcp連接中;strings包主要是對(duì)讀取到的數(shù)據(jù)的字符串形式進(jìn)行處理,比如去除掉字符串的一些符號(hào)等等。
下面的代碼需要先運(yùn)行服務(wù)端的代碼,再運(yùn)行客戶端的代碼:
TCP服務(wù)端
package main import ( "bufio" "fmt" "net" "os" "strings" ) // TCP 服務(wù)端 func process(conn net.Conn) { // 函數(shù)執(zhí)行完之后關(guān)閉連接 defer conn.Close() // 輸出主函數(shù)傳遞的conn可以發(fā)現(xiàn)屬于*TCPConn類型, *TCPConn類型那么就可以調(diào)用*TCPConn相關(guān)類型的方法, 其中可以調(diào)用read()方法讀取tcp連接中的數(shù)據(jù) fmt.Printf("服務(wù)端: %T\n", conn) for { var buf [128]byte // 將tcp連接讀取到的數(shù)據(jù)讀取到byte數(shù)組中, 返回讀取到的byte的數(shù)目 n, err := conn.Read(buf[:]) if err != nil { // 從客戶端讀取數(shù)據(jù)的過程中發(fā)生錯(cuò)誤 fmt.Println("read from client failed, err:", err) break } recvStr := string(buf[:n]) fmt.Println("服務(wù)端收到客戶端發(fā)來的數(shù)據(jù):", recvStr) // 由于是tcp連接所以雙方都可以發(fā)送數(shù)據(jù), 下面接收服務(wù)端發(fā)送的數(shù)據(jù)這樣客戶端也可以收到對(duì)應(yīng)的數(shù)據(jù) inputReader := bufio.NewReader(os.Stdin) s, _ := inputReader.ReadString('\n') t := strings.Trim(s, "\r\n") // 向當(dāng)前建立的tcp連接發(fā)送數(shù)據(jù), 客戶端就可以收到服務(wù)端發(fā)送的數(shù)據(jù) conn.Write([]byte(t)) } } func main() { // 監(jiān)聽當(dāng)前的tcp連接 listen, err := net.Listen("tcp", "127.0.0.1:20000") fmt.Printf("服務(wù)端: %T=====\n", listen) if err != nil { fmt.Println("listen failed, err:", err) return } for { conn, err := listen.Accept() // 建立連接 fmt.Println("當(dāng)前建立了tcp連接") if err != nil { fmt.Println("accept failed, err:", err) continue } // 對(duì)于每一個(gè)建立的tcp連接使用go關(guān)鍵字開啟一個(gè)goroutine處理 go process(conn) } }
TCP客戶端
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { // 連接到服務(wù)端建立的tcp連接 conn, err := net.Dial("tcp", "127.0.0.1:20000") // 輸出當(dāng)前建Dial函數(shù)的返回值類型, 屬于*net.TCPConn類型 fmt.Printf("客戶端: %T\n", conn) if err != nil { // 連接的時(shí)候出現(xiàn)錯(cuò)誤 fmt.Println("err :", err) return } // 當(dāng)函數(shù)返回的時(shí)候關(guān)閉連接 defer conn.Close() // 獲取一個(gè)標(biāo)準(zhǔn)輸入的*Reader結(jié)構(gòu)體指針類型的變量 inputReader := bufio.NewReader(os.Stdin) for { // 調(diào)用*Reader結(jié)構(gòu)體指針類型的讀取方法 input, _ := inputReader.ReadString('\n') // 讀取用戶輸入 // 去除掉\r \n符號(hào) inputInfo := strings.Trim(input, "\r\n") // 判斷輸入的是否是Q, 如果是Q則退出 if strings.ToUpper(inputInfo) == "Q" { // 如果輸入q就退出 return } _, err = conn.Write([]byte(inputInfo)) // 發(fā)送數(shù)據(jù) if err != nil { return } buf := [512]byte{} // 讀取服務(wù)端發(fā)送的數(shù)據(jù) n, err := conn.Read(buf[:]) if err != nil { fmt.Println("recv failed, err:", err) return } fmt.Println("客戶端接收服務(wù)端發(fā)送的數(shù)據(jù): ", string(buf[:n])) } }
總結(jié)
原文鏈接:https://blog.csdn.net/qq_39445165/article/details/124393872
相關(guān)推薦
- 2022-07-11 Python中列表,元組,字典和集合的區(qū)別及它們之間的轉(zhuǎn)換_python
- 2022-05-02 C/C++的文件IO函數(shù)你知道嗎_C 語言
- 2023-07-26 TypeScript中的模塊與命名空間
- 2022-07-03 pandas選擇或添加列生成新的DataFrame操作示例_python
- 2022-09-11 Oracle學(xué)習(xí)筆記之視圖及索引的使用_oracle
- 2022-02-22 算出兩個(gè)時(shí)間范圍是否有交集(前后端通用算法)
- 2022-04-08 Swift使用表格組件實(shí)現(xiàn)單列表_Swift
- 2022-10-14 Mybatis一對(duì)多使用PageHelper分頁遇到的問題
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支