zoukankan      html  css  js  c++  java
  • golang长连接和短连接的学习

    • 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()+"进入房间"+"
    ")  //当有一个客户端进来之时,广播某某进入房间
    		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+"离开了房间"+"
    ")
    	}()
    	reader:=bufio.NewReader(conn)
    	for{
    		message,err:=reader.ReadString('
    ')  //读取直到输入中第一次发生 ‘
    ’
    		//因为按强制退出的时候,他就先发送换行,然后在结束
    		if message == "
    "{
    			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 + "
    ")
    		conn.Write(b)
    	}
    }
    
    // 获取服务端发送来的信息
    func onMessageRectived(conn *net.TCPConn){
    	reader := bufio.NewReader(conn)
    	for {
    		// var data string
    		msg,err := reader.ReadString('
    ')  //读取直到输入中第一次发生 ‘
    ’
    		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悟Desktopstudygo	esthttpSever>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
    
    • https://serholiu.com/go-http-client-keepalive

    • 输出结果说明,请参考这篇博文

    • golang的client实现长连接的方式

      • web Server 支持长连接。(golang默认支持长连接).client要和服务端响应之后,保持连接
      • 根据需求,加大:DefaultMaxIdleConnsPerHost或设置MaxIdleConnsPerHost
      • 读完Response Body再Close
    • 写的不对的地方,希望可以加微信讨论一下

  • 相关阅读:
    RadGrid Expand/Collapse on Row click
    AutoComplete Textbox with Additional Parameters From Database
    Combobox.Items中添加项Items
    JavaScript 处理字符串(操作字符串)
    用nettiers + svn + resharper + rad + ccNet开发前的准备工作
    Document.location.href和.replace的区别
    .net remoting的事务传播以及wcf分布式事务
    IDA反汇编/反编译静态分析iOS模拟器程序(三)函数表示与搜索函数
    [置顶] 一道有趣的逻辑题
    mini2440uboot移植基本操作指令
  • 原文地址:https://www.cnblogs.com/MyUniverse/p/11621169.html
Copyright © 2011-2022 走看看