前言:最近学习Go语言 尝试写了一个简单的文件同步,基于tcp的,功能比较简单。基于Go version 1 编译。
该程序的功能:
- 只能传输单一文件,文件名不能有特殊符号和空格
- 有serve端和client命令 使用同一个程序
- 不能同步目录结构
- 不能改名字
- 无法查看进度
该程序主要就是将指定的文件同步到新的服务器和指定的目录下面,并且该文件的时间和属性跟原来一样,可以使用rsync进行检测。
该程序名字 gosync
gosync.go
- package main
- import (
- "os"
- "fmt"
- "net"
- "flag"
- "time"
- "crypto/md5"
- "io"
- "strings"
- "strconv"
- )
- type sysFileInfo struct{
- fName string
- fSize int64
- fMtime time.Time
- fPerm os.FileMode
- fMd5 string
- fType bool
- }
- var (
- listenPort = flag.String( "port","7722","server listen port" )
- syncFile = flag.String( "file","","transfer file" )
- syncHost = flag.String( "host","","server host" )
- syncSer = flag.Bool( "d",false,"server mode")
- syncFold = flag.String( "dir","/tmp/gosync/","recive sync fold ")
- )
- func main(){
- flag.Parse()
- if *syncSer {
- servPort:=fmt.Sprintf( ":%s",*listenPort )
- l,err := net.Listen( "tcp",servPort )
- if err != nil{
- fmt.Println( "net failed",err )
- }
- err = os.MkdirAll( *syncFold , 0755)
- if err != nil{
- fmt.Println( err )
- }
- fmt.Println( "Start Service" )
- Serve( l )
- }else{
- destination:=fmt.Sprintf( "%s:%s",*syncHost,*listenPort )
- clientSend( *syncFile,destination)
- }
- }
- func clientSend(files string,destination string){
- fInfo:=getFileInfo( files)
- newName :=fmt.Sprintf( "%s",fInfo.fName)
- cmdLine:= fmt.Sprintf( "upload %s %d %d %d %s " ,newName,fInfo.fMtime.Unix(),fInfo.fPerm,fInfo.fSize,fInfo.fMd5)
- cn,err:=net.Dial( "tcp", destination)
- if err !=nil {
- fmt.Println( "connect error",err )
- return
- }
- defer cn.Close()
- cn.Write( []byte( cmdLine ) )
- cn.Write( []byte( "\r\n" ) )
- fileHandle,err := os.Open( files )
- if err != nil {
- fmt.Println("open ERROR",err)
- return
- }
- io.Copy( cn,fileHandle)
- for{
- buffer :=make( []byte,1024)
- num,err := cn.Read(buffer)
- if err == nil && num > 0{
- fmt.Println( string(buffer[ :num ]) )
- break
- }
- }
- }
- func getFileInfo( filename string) *sysFileInfo{
- fi,err:= os.Lstat( filename )
- if err != nil {
- fmt.Println("info ERROR",err)
- return nil
- }
- fileHandle,err := os.Open( filename )
- if err != nil {
- fmt.Println("open ERROR",err)
- return nil
- }
- h := md5.New()
- _,err = io.Copy( h,fileHandle )
- fileInfo := & sysFileInfo {
- fName : fi.Name(),
- fSize : fi.Size(),
- fPerm : fi.Mode().Perm(),
- fMtime: fi.ModTime(),
- fType : fi.IsDir(),
- fMd5 : fmt.Sprintf( "%x", h.Sum( nil )),
- }
- return fileInfo
- }
- func Serve( l net.Listener) {
- for{
- conn,err := l.Accept()
- if err != nil{
- if ne,ok := err.( net.Error );ok && ne.Temporary(){
- continue
- }
- fmt.Println( "network error",err )
- }
- go Handler(conn)
- }
- }
- func Handler( conn net.Conn) {
- defer conn.Close()
- state := 0
- var cmd *sysFileInfo
- var fSize int64
- var tempFileName string
- var n int64
- for {
- buffer :=make( []byte,2048)
- num,err := conn.Read(buffer)
- numLen:=int64( num )
- if err != nil && err != io.EOF {
- fmt.Println( "cannot read",err )
- }
- n=0
- if state == 0 {
- n,cmd = cmdParse( buffer[:num] )
- tempFileName = fmt.Sprintf( "%s.newsync",cmd.fName)
- fSize = cmd.fSize
- state = 1
- }
- if state == 1 {
- last := numLen
- if fSize <= numLen-n {
- last = fSize + n
- state = 2
- }
- err = writeToFile( buffer[int( n ):int( last )],tempFileName,cmd.fPerm )
- if err != nil{
- fmt.Println( "read num error : ",err )
- }
- fSize -=last-n
- if state == 2{
- os.Remove( cmd.fName)
- err = os.Rename( tempFileName,cmd.fName)
- if err != nil{
- fmt.Println( "rename ",tempFileName," to ",cmd.fName," failed" )
- }
- err = os.Chtimes( cmd.fName,time.Now(),cmd.fMtime )
- if err != nil{
- fmt.Println( "change the mtime error ",err )
- }
- fileHandle,err := os.Open( cmd.fName)
- if err != nil {
- fmt.Println("open ERROR",err)
- }
- h := md5.New()
- io.Copy( h,fileHandle )
- newfMd5 := fmt.Sprintf( "%x", h.Sum( nil ))
- if newfMd5 == cmd.fMd5{
- sendInfo:=fmt.Sprintf("%s sync success",cmd.fName)
- conn.Write([]byte(sendInfo))
- }else{
- sendInfo:=fmt.Sprintf("%s sync failed",cmd.fName)
- conn.Write([]byte(sendInfo))
- }
- }
- }
- }
- }
- func cmdParse( infor []byte) ( int64 , *sysFileInfo) {
- var i int64
- for i=0;i<int64(len(infor));i++ {
- if infor[i] == '\n' && infor[i-1] == '\r' {
- cmdLine:=strings.Split( string( infor[:i-1] ) ," ")
- fileName := fmt.Sprintf( "%s/%s",*syncFold,cmdLine[ 1 ] )
- filePerm, _ := strconv.Atoi( cmdLine[ 3 ])
- fileMtime,_:= strconv.ParseInt( cmdLine[ 2 ],10,64 )
- fileSize,_:= strconv.ParseInt( cmdLine[ 4 ],10,64)
- fileInfo := & sysFileInfo {
- fName : fileName,
- fMtime: time.Unix( fileMtime,0 ),
- fPerm : os.FileMode(filePerm),
- fSize : fileSize,
- fMd5 : string(cmdLine[ 5 ]),
- }
- return i+1,fileInfo
- }
- }
- return 0,nil
- }
- func writeToFile( data []byte ,fileName string,perm os.FileMode) error{
- writeFile,err := os.OpenFile( fileName,os.O_RDWR | os.O_APPEND | os.O_CREATE ,perm)
- if err != nil{
- fmt.Println( "write file error:",err )
- return err
- }
- defer writeFile.Close()
- _,err = writeFile.Write( data )
- if err != nil{
- fmt.Println( "write file error",err )
- return err
- }
- return nil
- }
使用:
usage:
Usage of ./gosync:
-d=false: server mode
-dir="/tmp/gosync/": recive sync fold
-file="": transfer file
-host="": server host
-port="7722": server listen port
服务端开启
#./gosync -d -dir /tmp/xxxx
client传输文件:
# ./gosync --file=/root/Videos/DSCF2394.avi --host=127.0.0.1 --port=7722
/tmp/xxxx/DSCF2394.avi sync success
手动检查文件正确性:
# md5sum /tmp/xxxx/DSCF2394.avi
eb50332d3b3b6f36b773046aca16e908 /tmp/xxxx/DSCF2394.avi
# md5sum /root/Videos/DSCF2394.avi
eb50332d3b3b6f36b773046aca16e908 /root/Videos/DSCF2394.avi
转:
阅读(3211) | 评论(0) | 转发(0) |