-
TCP連接示意圖
-
長連接和短鏈接的區別
- 客戶端和服務端響應的次數
- 長連接:可以多次。
- 短鏈接:一次。
- 傳輸數據的方式
- 長連接:連接--數據傳輸--保持連接
- 短連接:連接--數據傳輸--關閉連接
- 客戶端和服務端響應的次數
-
長連接和短鏈接的優缺點
- 長連接
- 優點
- 省去較多的TCP建立和關閉的操作,從而節約時間。
- 性能比較好。(因為客戶端一直和服務端保持聯系)
- 缺點
- 當客戶端越來越多的時候,會將服務器壓垮。
- 連接管理難。
- 安全性差。(因為會一直保持着連接,可能會有些無良的客戶端,隨意發送數據等)
- 優點
- 短鏈接
- 優點
- 服務管理簡單。存在的連接都是有效連接
- 缺點
- 請求頻繁,在TCP的建立和關閉操作上浪費時間
- 優點
- 長連接
-
長連接和短連接使用情況舉例
- 長連接
- 微信/qq
- 一些游戲
- 短連接
- 普通的web網站
- 長連接
-
golang實現長連接參考代碼(實現群聊天)
server.go
package main
import(
"fmt"
"net"
"bufio"
"errors"
)
var connSlice []*net.TCPConn
// 創建TCP長連接服務
func createTcp(){
tcpAdd,err:= net.ResolveTCPAddr("tcp","127.0.0.1:9999") //解析tcp服務
if err!=nil{
fmt.Println("net.ResolveTCPAddr error:",err)
return
}
tcpListener,err:=net.ListenTCP("tcp",tcpAdd) //監聽指定TCP服務
if err!=nil{
fmt.Println("net.ListenTCP error:",err)
return
}
defer tcpListener.Close()
for{
tcpConn,err:=tcpListener.AcceptTCP() //阻塞,當有客戶端連接時,才會運行下面
if err!=nil{
fmt.Println("tcpListener error :",err)
continue
}
fmt.Println("A client connected:",tcpConn.RemoteAddr().String())
boradcastMessage(tcpConn.RemoteAddr().String()+"進入房間"+"\n") //當有一個客戶端進來之時,廣播某某進入房間
connSlice = append(connSlice,tcpConn)
// 監聽到被訪問時,開一個協程處理
go tcpPipe(tcpConn)
}
}
// 對客戶端作出反應
func tcpPipe(conn *net.TCPConn){
ipStr := conn.RemoteAddr().String()
fmt.Println("ipStr:",ipStr)
defer func(){
fmt.Println("disconnected:",ipStr)
conn.Close()
deleteConn(conn)
boradcastMessage(ipStr+"離開了房間"+"\n")
}()
reader:=bufio.NewReader(conn)
for{
message,err:=reader.ReadString('\n') //讀取直到輸入中第一次發生 ‘\n’
//因為按強制退出的時候,他就先發送換行,然后在結束
if message == "\n"{
return
}
message = ipStr+"說:"+message
if err!=nil{
fmt.Println("topPipe:",err)
return
}
// 廣播消息
fmt.Println(ipStr,"說:",message)
err = boradcastMessage(message)
if err!=nil{
fmt.Println(err)
return
}
}
}
// 廣播數據
func boradcastMessage(message string)error{
b := []byte(message)
for i:=0;i<len(connSlice);i++{
fmt.Println(connSlice[i])
_,err := connSlice[i].Write(b)
if err!=nil{
fmt.Println("發送給",connSlice[i].RemoteAddr().String(),"數據失敗"+err.Error())
continue
}
}
return nil
}
// 移除已經關閉的客戶端
func deleteConn(conn *net.TCPConn)error{
if conn==nil{
fmt.Println("conn is nil")
return errors.New("conn is nil")
}
for i:= 0;i<len(connSlice);i++{
if(connSlice[i]==conn){
connSlice = append(connSlice[:i],connSlice[i+1:]...)
break
}
}
return nil
}
func main(){
fmt.Println("服務端")
createTcp()
// data := []string{"a","b"}
// data = append(data[:1],data[2:]...) //測試data[2:]...會不會因為超過范圍報錯
// fmt.Println(data)
}
**client.go**
package main
import(
"os"
"fmt"
"net"
"bufio"
)
// 客戶端連接服務端
func createSocket(){
tcpAdd,err := net.ResolveTCPAddr("tcp","127.0.0.1:9999") //解析服務端TCP地址
if err!=nil{
fmt.Println("net.ResolveTCPAddr error:",err)
return
}
conn,err := net.DialTCP("tcp",nil,tcpAdd) //raddr是指遠程地址,laddr是指本地地址,連接服務端
if err!=nil{
fmt.Println("net.DailTCP error:",err)
return
}
defer conn.Close()
fmt.Println("connected")
go onMessageRectived(conn) //讀取服務端廣播的信息
for {
// 自己發送的信息
var data string
fmt.Scan(&data)
if data == "quit"{
break
}
b := []byte(data + "\n")
conn.Write(b)
}
}
// 獲取服務端發送來的信息
func onMessageRectived(conn *net.TCPConn){
reader := bufio.NewReader(conn)
for {
// var data string
msg,err := reader.ReadString('\n') //讀取直到輸入中第一次發生 ‘\n’
fmt.Println(msg)
if err!=nil{
fmt.Println("err:",err)
os.Exit(1) //服務端錯誤的時候,就將整個客戶端關掉
}
}
}
func main(){
fmt.Println("開啟客戶端")
createSocket()
}
- golang的Http長連接方式
server.go
package main
import (
"fmt"
"net/http"
)
func main(){
fmt.Println("服務端")
http.HandleFunc("/PrintHello",PrintHello)
http.ListenAndServe(":8080",nil)
}
func PrintHello(w http.ResponseWriter,r *http.Request){
data := "hello word"
fmt.Println(r.RemoteAddr)
fmt.Fprintf(w,data)
}
client.go
package main
import(
"fmt"
"time"
"net/http"
"io/ioutil"
)
func main(){
fmt.Println("客戶端")
for{
go doGet(1)
go doGet(2)
time.Sleep(time.Second*3)
}
}
func doGet(a int){
res,err:=http.Get("http://localhost:8080/PrintHello")
if err!=nil{
fmt.Println(err)
return
}
defer res.Body.Close()
data,err := ioutil.ReadAll(res.Body)
if err!=nil{
fmt.Println(err)
return
}
fmt.Println("接受服務端發送數據:",a,string(data))
}
- 客戶端訪問服務端,服務端打印輸出
C:\Users\悟\Desktop\studygo\test\httpSever>go run main.go
服務端
GET
[::1]:60388
GET
[::1]:60389
GET
[::1]:60389
GET
[::1]:60388
GET
[::1]:60388
GET
[::1]:60389
GET
[::1]:60389
GET
[::1]:60388
GET
[::1]:60388
GET
[::1]:60389
GET
[::1]:60389
- 客戶端打印結果
客戶端
接受服務端發送數據: 2 hello word
接受服務端發送數據: 1 hello word
接受服務端發送數據: 2 hello word
接受服務端發送數據: 1 hello word
接受服務端發送數據: 1 hello word
接受服務端發送數據: 2 hello word
-
輸出結果說明,請參考這篇博文
-
golang的client實現長連接的方式
- web Server 支持長連接。(golang默認支持長連接).client要和服務端響應之后,保持連接
- 根據需求,加大:DefaultMaxIdleConnsPerHost或設置MaxIdleConnsPerHost
- 讀完Response Body再Close
-
寫的不對的地方,希望可以加微信討論一下