第十一章 網絡編程

在TCP/IP協議中,“IP地址+TCP或UDP端口號”唯一標識網絡通訊中的一個進程。

因此可以用Socket來描述網絡連接的一對一關系。

常用的Socket類型有兩種:流式Socket(SOCK_STREAM)和數據報式Socket(SOCK_DGRAM)。流式是一種面向連接的Socket,針對于面向連接的TCP服務應用;數據報式Socket是一種無連接的Socket,對應于無連接的UDP服務應用。

1 TCP的C/S架構

1.1 單服務版本

服務端代碼:

package main  import (  "fmt"  "net")  func main() {  // 創建監控  listener, err := net.Listen("tcp", "localhost:8000")  if err != nil {  fmt.Println("listen err:", err)  return  }  defer listener.Close()  // 主協程結束時,關閉listener  fmt.Println("服務器等待客戶端建立連接...")  // 等待客戶端連接請求  conn, err := listener.Accept()  if err != nil {  fmt.Println("accpet err:", err)  return  }  defer conn.Close()  // 使用結束,斷開與客戶端鏈接  fmt.Println("客戶端與服務器連接建立成功...")  // 接收客戶端數據  buf := make([]byte, 1024)  // 創建1024大小的緩沖區,用于read  n, err := conn.Read(buf)  // 讀取到n個大小的數據  if err != nil {  fmt.Println("read err:", err)  return  }  fmt.Println("服務器讀到:", string(buf[:n])) // 讀多少,打印多少  
}

如圖,在整個通信過程中,服務器端有兩個socket參與進來,但用于通信的只有 conn 這個socket。它是由 listener創建的。隸屬于服務器端。

客戶端代碼:

package main  import (  "fmt"  "net")  func main() {  // 主動發送連接請求  conn, err := net.Dial("tcp", "localhost:8000")  if err != nil {  fmt.Println("Dial err", err)  }  defer conn.Close()  // 結束時,關閉連接  // 發送數據  _, err = conn.Write([]byte("Are u ready?"))  if err != nil {  fmt.Println("Write err:", err)  return  }  
}

1.2 并發服務

并發服務端:

Accept()函數的作用是等待客戶端的鏈接,如果客戶端沒有鏈接,該方法會阻塞。如果有客戶端鏈接,那么該方法返回一個Socket負責與客戶端進行通信。所以,每來一個客戶端,該方法就應該返回一個Socket與其通信,因此,可以使用一個死循環,將Accept()調用過程包裹起來。

需要注意的是,實現并發處理多個客戶端數據的服務器,就需要針對每一個客戶端連接,單獨產生一個Socket,并創建一個單獨的goroutine與之完成通信。

在判斷客戶端數據是否為“exit”字符串時,要注意,客戶端會自動的多發送2個字符:KaTeX parse error: Undefined control sequence: \n at position 4: “\r\?n?”(這在windows系統下代表回車、換行)

服務端代碼:

package main  import (  "fmt"  "net"   "strings")  func main() {  // 創建監控  listener, err := net.Listen("tcp", "localhost:8000")  if err != nil {  fmt.Println("listen err:", err)  return  }  defer listener.Close()  // 主協程結束時,關閉listener  for {  // 等待客戶端連接請求  conn, err := listener.Accept()  if err != nil {  fmt.Println("accpet err:", err)  return  }  // 處理用戶請求,新建一個協程  go HandleConn(conn)  }  
}  // 處理用戶請求  
func HandleConn(conn net.Conn) {  // 函數調用完畢,自動關閉conn  defer conn.Close()  // 獲取客戶端發過來的網址信息  addr := conn.RemoteAddr().String()  fmt.Println(addr, "connect successful")  buf := make([]byte, 2048)  for {  // 讀取用戶數據  n, err := conn.Read(buf)  if err != nil {  fmt.Println("err=", err)  return  }  fmt.Printf("[%s]: %s\n",  addr,  string(buf[:n]))  fmt.Println("len = ", len(string(buf[:n])))  //if string(buf[:n-1]) == "exit" // nc測試,發送時,只有/n  if string(buf[:n-2]) == "exit" {  fmt.Println(addr, "exit")  return  }  // 將數據轉化為大寫,再給用戶發送  conn.Write([]byte(strings.ToUpper(string(buf[:n]))))  }  
}

并發客戶端:

客戶端不僅需要持續的向服務端發送數據,同時也要接收從服務端返回的數據。因此可將發送和接收放到不同的協程中。

主協程循環接收服務器回發的數據(該數據應已轉換為大寫),并打印至屏幕;子協程循環從鍵盤讀取用戶輸入數據,寫給服務器。讀取鍵盤輸入可使用 os.Stdin.Read(str)。定義切片str,將讀到的數據保存至str中。

這樣,客戶端也實現了多任務。

package main  import (  "fmt"  "net"   "os")  func main() {  // 主動發送連接請求  conn, err := net.Dial("tcp", "localhost:8000")  if err != nil {  fmt.Println("Dial err", err)  }  defer conn.Close()  // 客戶端終止時,關閉于服務器通訊的socket  // 啟動子協程: 接受用戶鍵盤輸入發送給服務端  go func() {  // 創建用于存儲用戶鍵盤輸入數據的切片緩沖區str := make([]byte, 1024)    for {  // 反復讀取  n, err := os.Stdin.Read(str)  // 獲取用戶鍵盤輸入(阻塞)  if err != nil {  fmt.Println("os.Stdin.Read err:", err)  return  }  // 從鍵盤讀到的數據,發送給服務端  _, err = conn.Write(str[:n])  if err != nil {  fmt.Println("conn.Write err:", err)  return  }  }  }()  // 主協程: 接受服務端數據,進行打印輸出  buf := make([]byte, 1024)  // 定義用于存儲服務器回發數據的切片緩沖區  for {  n, err := conn.Read(buf)  // 從通信socket中讀數據,存入切片緩沖區(阻塞)  if err != nil {  fmt.Println("conn.Read err:", err)  return  }  fmt.Printf("服務器回發: %s\n", string(buf[:n]))  }  }

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901535.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901535.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901535.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ffmpeg實現視頻流抽幀

ffmpeg 實現視頻流抽幀 抽取實時視頻幀 如果你的實時視頻是通過 RTSP、UDP 或其他協議獲取的,可以直接調用 FFmpeg 命令來抽取幀。 ffmpeg 命令 示例 1 ffmpeg -i rtsp://your_rtsp_stream_url -vf fps1 -update 1 output.jpg說明: -i rtsp://your…

【GIT】放棄”本地更改,恢復到遠程倉庫的狀態git fetch origin git reset --hard origin/分支名

如果你想完全放棄本地更改,恢復到遠程倉庫的狀態,可以按照以下步驟操作: 獲取遠程最新版本 首先執行: git fetch origin這條命令會把遠程倉庫的最新提交拉取到你的本地,但不會自動合并到你的當前分支。 硬重置你的當前…

flutter doctor 信號號超時

報錯如下: :\Users\Administrator>flutter doctor Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.27.4, on Microsoft Windows [版本 10.0.22631.5189], locale zh-CN) [√] Windows Version (Installed versi…

【Linux】系統入門

【Linux】系統初識 起源開源 閉源版本內核內核編號 Linux的安裝雙系統(不推薦)WindowsLinuxvmware虛擬機vitualbox操作系統的鏡像centos 7/ubuntu云服務器租用 Linux的操作lsmkdir 文件名pwdadduser userdel -rrm文件名cat /proc/cpuinfolinux支持編程vim code.c./a.out 運行程…

mybatis-plus整合springboot與使用方式

注解 TableField(exist false)&#xff1a;表示該屬性不為數據庫表字段&#xff0c;但又是必須使用的。 整合springboot pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xs…

[第十六屆藍橋杯 JavaB 組] 真題 + 經驗分享

A&#xff1a;逃離高塔(AC) 這題就是簡單的簽到題&#xff0c;按照題意枚舉即可。需要注意的是不要忘記用long&#xff0c;用int的話會爆。 &#x1f4d6; 代碼示例&#xff1a; import java.io.*; import java.util.*; public class Main {public static PrintWriter pr ne…

GPU服務器聲音很響可以怎么處理

當GPU服務器運行時噪音過大&#xff0c;通常是由于高負載下散熱風扇高速運轉所致。以下是分步驟的解決方案&#xff0c;幫助您有效降低噪音并保持設備穩定運行&#xff1a; 一、排查噪音來源 定位聲源 ? 使用 聲級計 或手機分貝檢測APP&#xff0c;確定最大噪音位置&#xff0…

STM32平衡車開發實戰教程:從零基礎到項目精通

STM32平衡車開發實戰教程&#xff1a;從零基礎到項目精通 一、項目概述與基本原理 1.1 平衡車工作原理 平衡車是一種基于倒立擺原理的兩輪自平衡小車&#xff0c;其核心控制原理類似于人類保持平衡的過程。當人站立不穩時&#xff0c;會通過腿部肌肉的快速調整來維持平衡。平…

C#設計模式-狀態模式

狀態模式案例解析&#xff1a;三態循環燈的實現 案例概述 本案例使用 狀態模式&#xff08;State Pattern&#xff09; 實現了一個 三態循環燈 的功能。每點擊一次按鈕&#xff0c;燈的狀態會按順序切換&#xff08;狀態1 → 狀態2 → 狀態3 → 狀態1...&#xff09;&#xff…

Mac系統升級node.js版本和npm版本并安裝pnpm

1.升級node.js版本 第一步&#xff1a;查詢當前node.js版本 node -v第二步&#xff1a;清除node.js的緩存 sudo npm cache clean -f第三步&#xff1a;驗證緩存是否清空 npm cache verify第四步&#xff1a;安裝n工具&#xff0c;n工具是專門用于管理node.js版本的工具 su…

[net 5] udp_dict_server 基于udp的簡單字典翻譯(服務器與業務相分離)

目錄 1. 功能了解 1.1. 啥是 dic_server? 1.2. dic_server 的小目標 2. 基本框架 2.1. 基本文件框架 2.2. 業務與服務器解耦 -> 回調函數 3. 字典 3.1. 字典配置文件 3.2. 構建字典類 3.2.1. 字典類的基本成員 3.2.2. 字典類構造 3.2.2.1. 構造 3.2.2.2. 信息加…

七種驅動器綜合對比——《器件手冊--驅動器》

九、驅動器 名稱 功能與作用 工作原理 優勢 應用 隔離式柵極驅動器 隔離式柵極驅動器用于控制功率晶體管&#xff08;如MOSFET、IGBT、SiC或GaN等&#xff09;的開關&#xff0c;其核心功能是將控制信號從低壓側傳輸到高壓側的功率器件柵極&#xff0c;同時在輸入和輸出之…

EM儲能網關ZWS智慧儲能云應用(8) — 電站差異化支持

面對不同項目、種類繁多的儲能產品&#xff0c;如何在儲能云平臺上進行電站差異化支持尤為關鍵&#xff0c;ZWS智慧儲能云從多方面支持儲能電站差異化。 簡介 隨著行業發展&#xff0c;市場“內卷”之下&#xff0c;各大儲能企業推陳出新的速度加快。面對不同項目、種類繁多…

圖像預處理-色彩空間補充,灰度化與二值化

一.圖像色彩空間轉換 1.1 HSV顏色空間 HSV顏色空間使用色調&#xff08;Hue&#xff09;、飽和度&#xff08;Saturation&#xff09;和亮度&#xff08;Value&#xff09;三個參數來表示顏色 一般對顏色空間的圖像進行有效處理都是在HSV空間進行的&#xff0c;然后對于基本…

Midnight Flag CTF 2025

周末還是三個比賽&#xff0c;可惜不好弄。不是遠端連不上就是遠端打不開。再有就是太難了。 Crypto ABC 這個題還是不算難的。給了兩個30位數的平方和&#xff0c;并且pu1*baser0,qu2*baser1其中r 都很小&#xff0c;可以copper。 只是sage里的two_squres不管用&#xff0…

深度學習--激活函數

激活函數通過計算加權和并加上偏置來確定神經元是否應該倍激活&#xff0c;它們將輸入信號轉換為輸出的可微運算。大多數激活函數都是非線性的&#xff0c;由于激活函數是深度學習的基礎&#xff0c;下面簡要介紹一些常見的激活函數。 1 RelU函數 最受歡迎的激活函數是修正線性…

深入解析 OrdinalEncoder 與 OneHotEncoder:核心區別與實戰應用

標題&#xff1a;深入解析 OrdinalEncoder 與 OneHotEncoder&#xff1a;核心區別與實戰應用 摘要&#xff1a; 本文詳細探討了機器學習中類別特征編碼的兩種核心方法——OrdinalEncoder 和 OneHotEncoder。通過對比兩者的功能、特點、適用場景及代碼實現&#xff0c;幫助讀者…

CTF web入門之命令執行 完整版

web29 文件名過濾 由于flag被過濾,需要進行文件名繞過,有以下幾種方法: 1.通配符繞過 fla?.* 2.反斜杠繞過 fl\ag.php 3.雙引號繞過 fl’‘ag’.php 還有特殊變量$1、內聯執行等 此外 讀取文件利用cat函數,輸出利用system、passthru 、echo echo `nl flag.php`; ec…

【Linux實踐系列】:用c/c++制作一個簡易的進程池

&#x1f525; 本文專欄&#xff1a;Linux Linux實踐項目 &#x1f338;作者主頁&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客勵志語錄&#xff1a; 人生沒有標準答案&#xff0c;你的錯題本也能寫成傳奇。 ★★★ 本文前置知識&#xff1a; 匿名管道 1.前置知識回顧…

2.2 函數返回值

1.回顧def def sum(x,y): return xy res sum(10,20) #調用函數 print(res) 2.函數的三個重要屬性 -函數的類型&#xff1a;function -函數的ID&#xff1a;16進制的整數數值 -函數的值&#xff1a;封裝在函數中的數據和代碼 # - 函數是一塊內存空間&#xff0c;通過…