duanhuan1147 2018-09-08 08:49
浏览 405
已采纳

尝试在Golang中使用io.CopyN()读取时客户端卡住了

I am trying to make TCP server for transferring files. I am suing io.CopyN for reading and writing. From server side, I am sending files to client so from server side, it sends perfectly all bytes but Client side after reading a couple of 1000000 bytes it stuck. sometimes it works fine and sometimes it gets stuck. I am using 300 MB pdf to test. Any help, code, and output is like below.

server

    package main

    import (
        "fmt"
        "io"
        "log"
        "net"
        "os"
        "strconv"
        "strings"
    )

    func main() {

        ls, err := net.Listen("tcp", ":1234")

        errFunc(err)

        defer ls.Close()

        conn, _ := ls.Accept()

        defer conn.Close()

        for {

            file, err := os.Open(strings.TrimSpace("./" + "Mag" + ".pdf"))

            errFunc(err)

            defer file.Close()

            fileInfo, err := file.Stat()

            errFunc(err)

            size := fileInfo.Size()

            numberOfTime := size / 1000000

            leftByte := size - numberOfTime*1000000

            numberOfTimeString := strconv.Itoa(int(numberOfTime))
            leftByteString := strconv.Itoa(int(leftByte))

            fmt.Println("1000000 times : ", numberOfTimeString)

            fmt.Println("Left Bytes : ", leftByteString)

            _, err = fmt.Fprintf(conn, numberOfTimeString+"
")

            errFunc(err)

            _, err = fmt.Fprintf(conn, leftByteString+"
")

            errFunc(err)

            fileWriter := io.Writer(conn)

            for i := 0; i < int(numberOfTime); i++ {

                n, err := io.CopyN(conn, file, 1000000)

                if i >= 30 {
                    fmt.Println(err, n)
                }
            }

            n, err := io.CopyN(fileWriter, file, leftByte+1)

            if err == io.EOF {
                fmt.Println(err, n)
            }

            fmt.Printf("Succefully bytes sent : %v 




", n)

            file.Close()

        }

    }

    func errFunc(err error) {

        if err != nil {
            log.Fatal(err)
        }

    }

client

    package main

    import (
        "bufio"
        "fmt"
        "io"
        "net"
        "os"
        "os/signal"
        "strconv"
        "strings"
        "syscall"
    )

    func main() {

        c := make(chan os.Signal, 15)
        signal.Notify(c, syscall.SIGINT)

        go func() {

            for {
                s := <-c

                switch s {

                case syscall.SIGINT:
                    os.Exit(1)
                }

            }

        }()

        conn, _ := net.Dial("tcp", ":1234")

        defer conn.Close()

        connReadWrite := bufio.NewReader(io.Reader(conn))

        var i int
        var filename string

        for {

            i++

            nu := strconv.Itoa(i)

            filename = "image" + nu + ".pdf"

            file, err := os.Create(filename)

            defer file.Close()

            numberOfTimeString, err := connReadWrite.ReadString('
')

            if err != nil {

                fmt.Println(err)
            }

            println("1000000 times :", numberOfTimeString)

            numberOfTimeString = strings.TrimSuffix(numberOfTimeString, "
")

            numberOfTime, err := strconv.Atoi(numberOfTimeString)

            if err != nil {

                fmt.Println(err)
            }

            leftByteString, err := connReadWrite.ReadString('
')

            if err != nil {

                println(err)
            }

            println("Left Bytes :", leftByteString)

            leftByteString = strings.TrimSuffix(leftByteString, "
")

            leftByte, err := strconv.Atoi(leftByteString)

            if err != nil {

                panic(err)
            }

            fmt.Println("After convert in Num :", numberOfTime, leftByte)

            newFileWriter := io.Writer(file)
            newFileReader := io.Reader(conn)

            for i := 0; i < numberOfTime; i++ {

                n, err := io.CopyN(newFileWriter, newFileReader, 1000000)

                if i >= 30 {
                    errFun(err, n)
                }
            }

            n, err := io.CopyN(newFileWriter, newFileReader, int64(leftByte))

            errFun(err, n)

            fmt.Printf("sucessfully Transfered ---> 





")

        }

    }

    func errFun(err error, n int64) {

        if err == io.EOF {

            fmt.Println("End of file : ", n)
            return

        } else if n == 0 {

            fmt.Println("n is : ", n)
            return

        } else if err != nil {
            fmt.Println(err)
            return

        }

        fmt.Println(err, " : ", n)
    }

input/output

from server side first we are sending number of bytes it need to readand then client side it gets a number of bytes it needs to read and then I am sending the file and then it read. In the picture, I was able to send one-time second time it got stuck sometimes it stuck first time too.I am able to send number of byte from server side second time too but as you can see it don't read that numeber, it read something "%PDF..." and it even don't print "100000 times : " correctly it prints "%???00 times :" I just don’t understand this

enter image description here

  • 写回答

1条回答 默认 最新

  • dongze8698 2018-09-08 21:25
    关注

    I believe the issue is that you're using a bytes.Buffer in the client:

    connReadWrite := bufio.NewReader(io.Reader(conn))
    

    But you aren't using it later with the CopyN:

    newFileWriter := io.Writer(file)
    newFileReader := io.Reader(conn)
    for i := 0; i < numberOfTime; i++ {
        _, err := io.CopyN(newFileWriter, newFileReader, 1000000)
        if err != nil {
            log.Fatalln(err)
        }
    }
    

    Using:

    newFileWriter := io.Writer(file)
    
    for i := 0; i < numberOfTime; i++ {
        _, err := io.CopyN(file, connReadWrite, 1000000)
        if err != nil {
            log.Fatalln(err)
        }
    }
    

    May fix it.

    If you have control over the protocol you are using to send the file, I recommend doing something simpler. For example using the big-endian int64 length prefix.

    Send:

    func sendFile(name string, conn net.Conn) error {
        f, err := os.Open(name)
        if err != nil {
            return err
        }
        defer f.Close()
    
        fi, err := f.Stat()
        if err != nil {
            return err
        }
        sz := fi.Size()
    
        buf := bufio.NewWriter(conn)
    
        err = binary.Write(buf, binary.BigEndian, sz)
        if err != nil {
            return err
        }
    
        _, err = io.CopyN(buf, f, sz)
        if err != nil {
            return err
        }
    
        return buf.Flush()
    }
    

    Receive:

    func recvFile(name string, conn net.Conn) error {
        f, err := os.Create(name)
        if err != nil {
            return err
        }
        defer f.Close()
    
        buf := bufio.NewReader(conn)
        var sz int64
        err = binary.Read(buf, binary.BigEndian, &sz)
        if err != nil {
            return err
        }
    
        _, err = io.CopyN(f, buf, sz)
        if err != nil {
            return err
        }
    
        return nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 python代码,帮调试
  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条