zoukankan      html  css  js  c++  java
  • golang文件传输服务

    续上篇,本篇介绍一个完整的golang文件传输服务器。

    完整的代码可以看服务器,客户端

    网络使用的框架如上篇介绍,这里就不再复述.

    首先定义3个命令码:

    const (
        request_file = 1
        file_size = 2
        transfering = 3
    )

    request_file用于请求文件传输,附带的命令参数是文件key.

    file_size用于通告客户端文件的大小.

    transfering用于传输文件内容,附带参数是文件内容的二进制数据.

    服务器的文件配置示例

    ../learnyouhaskell.pdf=haskell
    ../golang.1.1.2.chm=golang
    ../NodeJS.pdf=NodeJS

    上面的文件配置了3个文件可供传输=左边是文件路径,右边是请求文件时使用的key.

    服务器启动时首先调用loadfile将文件导入到内存中,然后根据定义的key,将文件内容插入到字典filemap中:

    func loadfile(){
        //从配置导入文件
        F,err := os.Open("./config.txt")
        if err != nil {
            fmt.Printf("config.txt open failed
    ")
            return
        }
        filemap = make(map[string][]byte)
        bufferReader := bufio.NewReader(F)
        eof := false
        for !eof {
            line,err := bufferReader.ReadString('
    ')
            if err == io.EOF{
                err = nil
                eof = true
            }else if err != nil{
                fmt.Printf("parse file error
    ")
                return
            }
            if len(line) > 1 {
                line = line[0:len(line)-1]//drop '
    '
                fileconfig := strings.Split(line,"=")
                if len(fileconfig) == 2 {
                    buf, err := ioutil.ReadFile(fileconfig[0])
                    if err != nil {
                        fmt.Printf("%s load error
    ",fileconfig[0])
                    }else{  
                        filemap[fileconfig[1]] = buf
                        fmt.Printf("%s load success,key %s
    ",fileconfig[0],fileconfig[1])
                    }
                }
            }
        }
    
        if filemap["golang"] == nil {
            fmt.Printf("golang not found
    ")
        }
    
        fmt.Printf("loadfile finish
    ") 
    }

    接着是服务其的packet_handler:

    func process_client(session *tcpsession.Tcpsession,rpk *packet.Rpacket){
        cmd,_ := rpk.Uint16()
        if cmd == request_file {
            if session.Ud() != nil {
                fmt.Printf("already in transfer session
    ")
            }else
            {
                filename,_ := rpk.String()
                filecontent := filemap[filename]
                if filecontent == nil {
                    fmt.Printf("%s not found
    ",filename)
                    session.Close()
                }else{
                    fmt.Printf("request file %s
    ",filename)
                    tsession := &transfer_session{filecontent:filecontent,ridx:0}
                    session.SetUd(tsession)
    
                    wpk := packet.NewWpacket(packet.NewByteBuffer(64),false)
                    wpk.PutUint16(file_size)
                    wpk.PutUint32(uint32(len(filecontent)))
                    session.Send(wpk,nil)
                    tsession.send_file(session)
                }   
            }
        }else{
            fmt.Printf("cmd error,%d
    ",cmd)
            session.Close()
        }
    }

    如果收到的消息是requestfile,首先查看请求的文件是否存在,如果存在则创建一个文件传输过程transfersession,

    并将它与tcpsession绑定,然后发出一个文件大小通告包,紧接着立即调用send_file开始发送文件内容.

    func (this *transfer_session)send_file(session *tcpsession.Tcpsession){
        remain := len(this.filecontent) - this.ridx
        sendsize := 0
        if remain >= 16000 {
            sendsize = 16000
        }else{
            sendsize = remain
        }
        wpk := packet.NewWpacket(packet.NewByteBuffer(uint32(sendsize)),false)
        wpk.PutUint16(transfering)
        wpk.PutBinary(this.filecontent[this.ridx:this.ridx+sendsize])
        session.Send(wpk,send_finish)
        this.ridx += sendsize
    }

    sendfile中根据当前发送位置判断还有多少内容需要发送,如果剩余内容小于16000字节就将所剩数据一次性

    发出,否则 发送16000字节的数据,并调整发送位置。注意到Send函数带了一个sendfinish函数作为参数,其作用

    是当数据包发送 完成后回调send_finish函数.

    func send_finish (s interface{},wpk *packet.Wpacket){
        session := s.(*tcpsession.Tcpsession)
        tsession := session.Ud().(*transfer_session)
        if tsession.check_finish(){
            session.Close()
            return
        }
        tsession.send_file(session)
    }

    send_finish的作用是判断文件是否已经发送完,如果发完断开连接,否则接着发送剩余部分.

    总结一下,golang用来编写服务器应用还是相当方便的,很多细节问题在语言层面或系统库里已经帮你解决掉了

    ,可以将主要的 精力放在逻辑的处理上.

  • 相关阅读:
    nyoj 199 无线网络覆盖
    hdoj 2682 Tree
    nyoj 845 无主之地1
    hdoj 1874 畅通工程续【dijkstra算法or spfa算法】
    hdoj 2544 最短路【dijkstra or spfa】
    hdoj 4548 美素数
    打表法
    hdoj 2098 分拆素数和
    hdoj 1262 寻找素数对
    bzoj1180,2843
  • 原文地址:https://www.cnblogs.com/sniperHW/p/3587313.html
Copyright © 2011-2022 走看看