golang socket断点续传大文件的实现方法
在日常编程中,我们肯定会遇到用socket传送文件内容,如果是大文件的,总不能传送到一半因某原因断掉了,又从新传送文件内容吧。对,我们需要续传,也就是接着上次传送的位置继续发送文件内容。
续传的话,其实并不难,我理解的思路大概如下:
客户端发送消息询问服务端,你上次接收到的文件内容位置
服务端告诉客户端上次接收到的文件内容位置
客户端就从上次断点的位置继续发送文件内容
客户端发送文件内容完毕后通知服务端,然后断开连接
下面我们看看代码的实现
服务端
//filename:server.go
packagemain
import(
"os"
"io"
"net"
"log"
"strconv"
//"time"
)
//把接收到的内容append到文件
funcwriteFile(content[]byte){
iflen(content)!=0{
fp,err:=os.OpenFile("test_1.txt",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0755)
deferfp.Close()
iferr!=nil{
log.Fatalf("openfilefaild:%s\n",err)
}
_,err=fp.Write(content)
iferr!=nil{
log.Fatalf("appendcontenttofilefaild:%s\n",err)
}
log.Printf("appendcontent:【%s】success\n",string(content))
}
}
//获取已接收内容的大小
//(断点续传需要把已接收内容大下通知客户端从哪里开始发送文件内容)
funcgetFileStat()int64{
fileinfo,err:=os.Stat("test_1.txt")
iferr!=nil{
//如果首次没有创建test_1.txt文件,则直接返回0
//告诉客户端从头开始发送文件内容
ifos.IsNotExist(err){
log.Printf("filesize:%d\n",0)
returnint64(0)
}
log.Fatalf("getfilestatfaild:%s\n",err)
}
log.Printf("filesize:%d\n",fileinfo.Size())
returnfileinfo.Size()
}
funcserverConn(connnet.Conn){
deferconn.Close()
for{
varbuf=make([]byte,10)
n,err:=conn.Read(buf)
iferr!=nil{
iferr==io.EOF{
log.Println("serverioEOF\n")
return
}
log.Fatalf("serverreadfaild:%s\n",err)
}
log.Printf("recevice%dbytes,contentis【%s】\n",n,string(buf[:n]))
//判断客户端发送过来的消息
//如果是'start-->‘则表示需要告诉客户端从哪里开始读取文件数据发送
switchstring(buf[:n]){
case"start-->":
off:=getFileStat()
//intconverstring
stringoff:=strconv.FormatInt(off,10)
_,err=conn.Write([]byte(stringoff))
iferr!=nil{
log.Fatalf("serverwritefaild:%s\n",err)
}
continue
case"<--end":
//如果接收到客户端通知所有文件内容发送完毕消息则退出
log.Fatalf("receiveover\n")
return
//default:
//time.Sleep(time.Second*1)
}
//把客户端发送的内容保存到文件
writeFile(buf[:n])
}
}
funcmain(){
//建立监听
l,err:=net.Listen("tcp",":8888")
iferr!=nil{
log.Fatalf("errorlisten:%s\n",err)
}
deferl.Close()
log.Println("waitingaccept.")
//允许客户端连接,在没有客户端连接时,会一直阻塞
conn,err:=l.Accept()
iferr!=nil{
log.Fatalf("acceptfaild:%s\n",err)
}
serverConn(conn)
}
客户端
//filename:client.go
packagemain
import(
"os"
"io"
"net"
"log"
"time"
"strconv"
)
//获取服务端发送的消息
funcclientRead(connnet.Conn)int{
buf:=make([]byte,5)
n,err:=conn.Read(buf)
iferr!=nil{
log.Fatalf("receiveserverinfofaild:%s\n",err)
}
//stringconverint
off,err:=strconv.Atoi(string(buf[:n]))
iferr!=nil{
log.Fatalf("stringconverintfaild:%s\n",err)
}
returnoff
}
//发送消息到服务端
funcclientWrite(connnet.Conn,data[]byte){
_,err:=conn.Write(data)
iferr!=nil{
log.Fatalf("send【%s】contentfaild:%s\n",string(data),err)
}
log.Printf("send【%s】contentsuccess\n",string(data))
}
//clientconn
funcclientConn(connnet.Conn){
deferconn.Close()
//发送"start-->"消息通知服务端,我要开始发送文件内容了
//你赶紧告诉我你那边已经接收了多少内容,我从你已经接收的内容处开始继续发送
clientWrite(conn,[]byte("start-->"))
off:=clientRead(conn)
//sendfilecontent
fp,err:=os.OpenFile("test.txt",os.O_RDONLY,0755)
iferr!=nil{
log.Fatalf("openfilefaild:%s\n",err)
}
deferfp.Close()
//setfileseek
//设置从哪里开始读取文件内容
_,err=fp.Seek(int64(off),0)
iferr!=nil{
log.Fatalf("setfileseekfaild:%s\n",err)
}
log.Printf("readfileatseek:%d\n",off)
for{
//每次发送10个字节大小的内容
data:=make([]byte,10)
n,err:=fp.Read(data)
iferr!=nil{
iferr==io.EOF{
//如果已经读取完文件内容
//就发送'<--end'消息通知服务端,文件内容发送完了
time.Sleep(time.Second*1)
clientWrite(conn,[]byte("<--end"))
log.Println("sendallcontent,nowquit")
break
}
log.Fatalf("readfileerr:%s\n",err)
}
//发送文件内容到服务端
clientWrite(conn,data[:n])
}
}
funcmain(){
//connecttimeout10s
conn,err:=net.DialTimeout("tcp",":8888",time.Second*10)
iferr!=nil{
log.Fatalf("clientdialfaild:%s\n",err)
}
clientConn(conn)
}
客户端读取文件test.txt内容发送到服务端,服务端把接收到的文件内容保存在test_1.txt文件中。我们模拟断点续传的方式是:
第一次先发送test.txt文件内容到服务端
修改test.txt文件,加一些内容
再次运行serversocket以及clientsocket,观察客户端是不是只发送新增的文件内容到服务端
#假设我的test.txt文件有以下内容 $cattest.txt hellogolang. #先运行serversocket再运行clientsocket(分别在两个终端窗口运行) $gorunserver.go $gorunclient.go #服务端会输出以下内容 2018/04/0523:37:13waitingaccept. 2018/04/0523:37:15recevice8bytes,contentis【start-->】 2018/04/0523:37:15filesize:0 2018/04/0523:37:15recevice10bytes,contentis【hellogola】 2018/04/0523:37:15appendcontent:【hellogola】success 2018/04/0523:37:15recevice2bytes,contentis【n.】 2018/04/0523:37:15appendcontent:【n.】success 2018/04/0523:37:16recevice6bytes,contentis【<--end】 2018/04/0523:37:16receiveover exitstatus1 #客户端会输出如下内容 2018/04/0523:37:15send【start-->】contentsuccess 2018/04/0523:37:15readfileatseek:0 2018/04/0523:37:15send【hellogola】contentsuccess 2018/04/0523:37:15send【n.】contentsuccess 2018/04/0523:37:16send【<--end】contentsuccess 2018/04/0523:37:16sendallcontent,nowquit #这时候我们看看test_1.txt内容跟test.txt完全一致 $cattest_1.txt hellogolan. #-------模拟断点续传---------- #现在我们往test.txt追加内容:hellopython. $cattest.txt hellogolang. hellopython. #我们再一次运行serversocket和clientsocket(分别在两个终端窗口运行) $gorunserver.go $gorunclient.go #服务端会输出以下内容 2018/04/0523:44:31waitingaccept. 2018/04/0523:44:34recevice8bytes,contentis【start-->】 2018/04/0523:44:34filesize:12 2018/04/0523:44:34recevice10bytes,contentis【 hellopyt】 2018/04/0523:44:34appendcontent:【 hellopyt】success 2018/04/0523:44:34recevice4bytes,contentis【hon.】 2018/04/0523:44:34appendcontent:【hon.】success 2018/04/0523:44:35recevice6bytes,contentis【<--end】 2018/04/0523:44:35receiveover exitstatus1 #服务端在接收到客户端发送的start-->信息后会获取上次接收到文件内容位置,并通知客户端(这里filesize是12) #客户端会输出以下内容 2018/04/0523:44:34send【start-->】contentsuccess 2018/04/0523:44:34readfileatseek:12 2018/04/0523:44:34send【 hellopyt】contentsuccess 2018/04/0523:44:34send【hon.】contentsuccess 2018/04/0523:44:35send【<--end】contentsuccess 2018/04/0523:44:35sendallcontent,nowquit #我们客户端获取到了服务端返回的文件位置,通过Seek来指定从哪里开始读取文件 #通过日志可以看到我们客户端只发送了后面追加的内容:hellopython.到服务端 #我们看看此时test_1.txt文件的内容是否跟test.txt一致 $cattest_1.txt hellogolang. hellopython.
以上这篇golangsocket断点续传大文件的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。