zoukankan      html  css  js  c++  java
  • golang初体验

    学习golang的时间断断续续加起来也有将近一个月了,这期间都是偶看翻几页书,没有写过实际的代码.最近做一个app项目,是一个展示类 的软件,当客户要看某个图片时首先向服务器发出一个请求,比对图片的版本,如果版本与本地一致,则直接显示,如果版本落后了则由服务 器将最新的版本发送给客户端.

    对服务器的需求就是一个简单的版本比对和文件传输,于是打算用go去实现,正好也可以练练手.

    在设计上,受到以往框架设计的影响,还是使用了wpacket,rpacket和共享buffer这个方案,不同的地方是go的网络API不支持 gather io,所以buffer没有被实现为buffer list,而是一整块的连续内存,当buffer空间不足时需要手动的去扩展内存.

    下面说点与以往用C设计网络框架不同的地方,对于已经习惯了使用异步回调方式来使用网络的我,刚开始用go来做一个网络框架还是稍有不适. go的网络API都是同步的,也不存在select,epoll,poll这类东西,这也就意味着需要使用goroutine来实现并发.

    我的做法是这样的,首先定义了一个类型Tcpconnection

    type Tcpsession struct{
        Conn net.Conn
        Packet_que chan interface{}
        Send_que chan *packet.Wpacket
        raw bool
        send_close bool
    }

    其中Conn成员是连接对象,Packet_que是一个管道,用于接收收到的网络包和连接事件.Send_que是另一个管道,用来将需要发送 的数据包传给sender。

    func NewTcpSession(conn net.Conn,raw bool)(*Tcpsession){
        session := &Tcpsession{Conn:conn,Packet_que:make(chan interface{},1024),Send_que:make(chan *packet.Wpacket,1024),raw:raw,send_close:false}
        if raw{
            go dorecv_raw(session)
        }else{
            go dorecv(session)
        }
        go dosend(session)
        return session
    }

    在建立一个新连接之后,启动两个goroutine分别用于执行recv和send.

    func dorecv_raw(session *Tcpsession){
        for{
            recvbuf := make([]byte,packet.Max_bufsize)
            _,err := session.Conn.Read(recvbuf)
            if err != nil {
                session.Packet_que <- "rclose"
                return
            }
            rpk := packet.NewRpacket(packet.NewBufferByBytes(recvbuf),true)
            session.Packet_que <- rpk
        }
    }
    
    func dosend(session *Tcpsession){
        for{
            wpk,ok :=  <-session.Send_que
            if !ok {
                return
            }
            _,err := session.Conn.Write(wpk.Buffer().Bytes())
            if err != nil {
                session.send_close = true
                return
            }
            if wpk.Fn_sendfinish != nil{
                wpk.Fn_sendfinish(session,wpk)
            }
        }
    }

    dorecv的工就只是简单的接收数据,将收到的原始数据打包成rpacket,然后写到管道Packet_que中。 dosend则是从'Send_que'中提取出要待发送的wpacket,然后send出去.

    然后来看下主过程:

    func main(){
        service := ":8010"
        tcpAddr,err := net.ResolveTCPAddr("tcp4", service)
        if err != nil{
            fmt.Printf("ResolveTCPAddr")
        }
        listener, err := net.ListenTCP("tcp", tcpAddr)
        if err != nil{
            fmt.Printf("ListenTCP")
        }
        for {
            conn, err := listener.Accept()
            if err != nil {
                continue
            }
            session := tcpsession.NewTcpSession(conn,true)
            fmt.Printf("a client comming
    ")
            go tcpsession.ProcessSession(session,process_client,session_close)
        }
    }

    主过程等待在listen上,每当接受一个新的连接就用这个连接作为参数创建一个Tcpsession,然后启动一个 goroutine运行ProcessSession

    func ProcessSession(tcpsession *Tcpsession,process_packet func (*Tcpsession,*packet.Rpacket),session_close func (*Tcpsession)){
        for{
            msg,ok := <- tcpsession.Packet_que
            if !ok {
                fmt.Printf("client disconnect
    ")
                return
            }
            switch msg.(type){
                case * packet.Rpacket:
                    rpk := msg.(*packet.Rpacket)
                    process_packet(tcpsession,rpk)
                case string:
                    str := msg.(string)
                    if str == "rclose"{
                        session_close(tcpsession)
                        close(tcpsession.Packet_que)
                        close(tcpsession.Send_que)
                        tcpsession.Conn.Close()
                        return
                    }
            }
        }
    }

    ProcessSession的工作就是不断的从Packet_que中取出消息,如果消息是一个rpacket就回调使用者传进来的process_packet函数。 如果是一个网络连接断开的事件则清理这个Tcpsession.

    对每个连接,使用3个goroutine,2个channel,一个goroutine用于发送,一个用于接收和拆包,一个用于处理数据包.这个模式基本上就是标准的 go模式了.至于性能,在测试程序中我没有启用goroutine在多核心上运行的特性,也就说整个进程就是一个单线程的程序.其效率并不比实现同样功能 的C程序差.

    项目地址

  • 相关阅读:
    使用jQuery操作DOM(ppt练习)
    使用jQuery操作dom(追加和删除样式-鼠标移入移出)练习
    Serializable接口
    过滤选择器(接上)
    内容过滤选择器
    基本过滤选择器
    IO流
    C语言打印100到200之间的素数
    续上篇结尾应用异或关系改变两个变量的值
    将两个整型变量的值互换的方法
  • 原文地址:https://www.cnblogs.com/sniperHW/p/3581249.html
Copyright © 2011-2022 走看看