1、思路
-
如何在Go中實(shí)現(xiàn)命令行輸入的非阻塞讀取
因?yàn)槿绻褂脗鹘y(tǒng)的阻塞式輸入(如fmt.Scanln),程序會(huì)一直等待用戶輸入,無(wú)法同時(shí)處理其他任務(wù)。因此,應(yīng)該使用并發(fā)機(jī)制,讓輸入監(jiān)聽(tīng)和處理SIP消息可以同時(shí)進(jìn)行。
-
如何監(jiān)聽(tīng)鍵盤(pán)輸入
在Go中,可以使用bufio.NewReader來(lái)讀取標(biāo)準(zhǔn)輸入,但通常這是阻塞的。為了非阻塞地讀取輸入,可能需要使用goroutine來(lái)單獨(dú)處理輸入,這樣主線程可以繼續(xù)執(zhí)行網(wǎng)絡(luò)請(qǐng)求。
-
如何將用戶的輸入與相應(yīng)的函數(shù)調(diào)用綁定
當(dāng)用戶輸入“r”時(shí),調(diào)用RequestRegister函數(shù);輸入“rm”時(shí),調(diào)用取消注冊(cè)的函數(shù);當(dāng)用戶輸入“q”,則退出循環(huán)。需要設(shè)計(jì)一個(gè)循環(huán),不斷監(jiān)聽(tīng)輸入,并根據(jù)輸入內(nèi)容觸發(fā)不同的操作。
在main函數(shù)中需要持續(xù)處理其他任務(wù),比如接收SIP消息。這意味著主函數(shù)不能因?yàn)榈却斎攵枞?。因此,使用goroutine來(lái)并發(fā)執(zhí)行輸入監(jiān)聽(tīng)和其他任務(wù)是一個(gè)合理的方案。
2、實(shí)現(xiàn)功能的步驟
啟動(dòng)一個(gè)goroutine來(lái)監(jiān)聽(tīng)鍵盤(pán)輸入:這個(gè)goroutine會(huì)不斷讀取用戶的輸入,并根據(jù)輸入內(nèi)容觸發(fā)相應(yīng)的函數(shù)調(diào)用。
在主goroutine中處理其他任務(wù):比如持續(xù)接收SIP消息,處理網(wǎng)絡(luò)請(qǐng)求等。
處理并發(fā)訪問(wèn)共享資源的問(wèn)題:如果RequestRegister或其他函數(shù)涉及到共享資源(比如全局的UDP連接),需要使用互斥鎖(sync.Mutex)來(lái)保證線程安全。
實(shí)現(xiàn)取消注冊(cè)函數(shù):用戶提到“rm”觸發(fā)取消注冊(cè)。需要設(shè)計(jì)一個(gè)函數(shù),比如RequestUnregister,用于發(fā)送取消注冊(cè)的SIP請(qǐng)求,類(lèi)似于注冊(cè)過(guò)程但使用不同的方法。
3、關(guān)鍵代碼
// 啟動(dòng)輸入監(jiān)聽(tīng)
go handleUserInput()
// 主循環(huán)處理網(wǎng)絡(luò)消息
for {
buffer := make([]byte, 2048)
n, remoteAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Printf("讀取錯(cuò)誤: %v", err)
continue
}
msg := string(buffer[:n])
switch {
case strings.HasPrefix(msg, "SIP/2.0 401"):
handle401Response(msg)
case strings.HasPrefix(msg, "NOTIFY"):
go handleNotify(msg, remoteAddr)
case strings.HasPrefix(msg, "SIP/2.0 200"):
log.Println("操作成功")
default:
log.Printf("收到未知消息: %.200s...", msg)
}
}
// 處理用戶輸入
func handleUserInput() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("命令提示:")
fmt.Println("r - 注冊(cè)")
fmt.Println("rm - 取消注冊(cè)")
fmt.Println("q - 退出程序")
for scanner.Scan() {
input := strings.TrimSpace(scanner.Text())
switch input {
case "r":
go func() {
connLock.Lock()
defer connLock.Unlock()
if !isRegistered {
RequestRegister()
isRegistered = true
}
}()
case "rm":
go func() {
connLock.Lock()
defer connLock.Unlock()
if isRegistered {
RequestUnregister()
isRegistered = false
}
}()
case "q":
os.Exit(0)
default:
fmt.Println("未知命令,可用命令:r/rm/q")
}
}
}
4、實(shí)現(xiàn)效果
-
命令行交互與網(wǎng)絡(luò)處理的完美并行
-
線程安全的共享資源訪問(wèn)
-
符合SIP標(biāo)準(zhǔn)的注冊(cè)/注銷(xiāo)流程
-
清晰的狀態(tài)管理機(jī)制