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用來編寫服務器應用還是相當方便的,很多細節問題在語言層面或系統庫里已經幫你解決掉了
,可以將主要的 精力放在邏輯的處理上.
總結
以上是生活随笔為你收集整理的golang文件传输服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安全公司警告有黑客利用 PuTTY /
- 下一篇: 路由器串第三个路由器怎么设置家里第三个路