├── README.md ├── doc.go ├── .gitignore ├── protocol └── protocol.go ├── filehandler └── filehandler.go ├── LICENSE ├── main.go └── sockhandler └── sockhandler.go /README.md: -------------------------------------------------------------------------------- 1 | # minifileserver 2 | A simple file download server(TCP) 3 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // minifileserver project doc.go 2 | 3 | /* 4 | minifileserver document 5 | */ 6 | package main 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | ) 8 | 9 | const ( 10 | MaxFileNameLen = 24 11 | ) 12 | 13 | type Msg struct { 14 | Signature uint32 15 | Cmd uint16 16 | Param int16 17 | FileName [MaxFileNameLen]byte 18 | Len int32 19 | } 20 | 21 | func (m *Msg) String() string { 22 | return fmt.Sprintf("Signature:%d Cmd:%d Param:%d FileName:%s Len:%d", m.Signature, m.Cmd, m.Param, m.FileName, m.Len) 23 | } 24 | 25 | func (m *Msg) Bytes() []byte { 26 | var buf bytes.Buffer 27 | binary.Write(&buf, binary.LittleEndian, m) 28 | return buf.Bytes() 29 | } 30 | 31 | const ( 32 | MsgSize = 4 + 2 + 2 + 24 + 4 33 | CustomSignature = 0xFAFBFCFD 34 | 35 | CM_PING = 100 36 | SM_PING = 200 37 | 38 | CM_GETFILE = 1000 39 | SM_GETFILE = 2000 40 | ) 41 | -------------------------------------------------------------------------------- /filehandler/filehandler.go: -------------------------------------------------------------------------------- 1 | package filehandler 2 | 3 | import ( 4 | "errors" 5 | "io/ioutil" 6 | "strings" 7 | "sync" 8 | 9 | . "github.com/ecofast/sysutils" 10 | ) 11 | 12 | type FileHandler struct { 13 | filePath string 14 | mutex sync.Mutex 15 | fileCaches map[string][]byte 16 | } 17 | 18 | func (fh *FileHandler) Initialize(filePath string) { 19 | fh.filePath = filePath 20 | fh.fileCaches = make(map[string][]byte) 21 | } 22 | 23 | func (fh *FileHandler) GetFile(filename string) ([]byte, error) { 24 | lowername := strings.ToLower(filename) 25 | fh.mutex.Lock() 26 | defer fh.mutex.Unlock() 27 | buf, ok := fh.fileCaches[lowername] 28 | if !ok { 29 | fullname = fh.filePath + filename 30 | if FileExists(fullname) { 31 | data, err := ioutil.ReadFile(fullname) 32 | if err != nil { 33 | return nil, err 34 | } 35 | fh.add(lowername, data) 36 | return data, nil 37 | } 38 | return nil, errors.New("The required file does not exists: " + filename) 39 | } 40 | return buf, nil 41 | } 42 | 43 | func (fh *FileHandler) add(filename string, filebytes []byte) { 44 | fh.fileCaches[filename] = filebytes 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017, 无尽愿 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "minifileserver/sockhandler" 7 | "os" 8 | "path/filepath" 9 | "time" 10 | 11 | . "github.com/ecofast/iniutils" 12 | . "github.com/ecofast/sysutils" 13 | ) 14 | 15 | const ( 16 | listenPort = 7000 17 | reportConnNumInterval = 10 * 60 18 | ) 19 | 20 | func main() { 21 | startService() 22 | } 23 | 24 | func startService() { 25 | port := listenPort 26 | filePath := GetApplicationPath() 27 | tickerInterval := reportConnNumInterval 28 | ininame := ChangeFileExt(os.Args[0], ".ini") 29 | if FileExists(ininame) { 30 | port = IniReadInt(ininame, "setup", "port", port) 31 | filePath = IncludeTrailingBackslash(IniReadString(ininame, "setup", "filepath", filePath)) 32 | tickerInterval = IniReadInt(ininame, "setup", "reportinterval", tickerInterval) 33 | } else { 34 | IniWriteInt(ininame, "setup", "port", port) 35 | IniWriteString(ininame, "setup", "filepath", filePath) 36 | IniWriteInt(ininame, "setup", "reportinterval", tickerInterval) 37 | } 38 | filePath = filepath.ToSlash(filePath) 39 | fmt.Printf("监听端口:%d\n", port) 40 | fmt.Printf("文件目录:%s\n", filePath) 41 | fmt.Printf("连接数报告间隔:%d\n", tickerInterval) 42 | 43 | ticker := time.NewTicker(time.Duration(tickerInterval) * time.Second) 44 | go func() { 45 | for range ticker.C { 46 | log.Printf("[Ticker] 当前连接数:%d\n", sockhandler.Conns.Count()) 47 | } 48 | }() 49 | 50 | sockhandler.Run(port, filePath) 51 | } 52 | -------------------------------------------------------------------------------- /sockhandler/sockhandler.go: -------------------------------------------------------------------------------- 1 | package sockhandler 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "minifileserver/filehandler" 8 | . "minifileserver/protocol" 9 | "net" 10 | "sync" 11 | 12 | . "github.com/ecofast/sysutils" 13 | ) 14 | 15 | type ActiveConns struct { 16 | mutex sync.Mutex 17 | conns map[string]net.Conn 18 | } 19 | 20 | func (cs *ActiveConns) Initialize() { 21 | cs.conns = make(map[string]net.Conn) 22 | } 23 | 24 | func (cs *ActiveConns) Add(addr string, conn net.Conn) { 25 | cs.mutex.Lock() 26 | defer cs.mutex.Unlock() 27 | cs.conns[addr] = conn 28 | } 29 | 30 | func (cs *ActiveConns) Remove(addr string) { 31 | cs.mutex.Lock() 32 | defer cs.mutex.Unlock() 33 | delete(cs.conns, addr) 34 | } 35 | 36 | func (cs *ActiveConns) Exists(addr string) bool { 37 | cs.mutex.Lock() 38 | defer cs.mutex.Unlock() 39 | _, ok := cs.conns[addr] 40 | return ok 41 | } 42 | 43 | func (cs *ActiveConns) Count() int { 44 | cs.mutex.Lock() 45 | defer cs.mutex.Unlock() 46 | return len(cs.conns) 47 | } 48 | 49 | const ( 50 | RecvBufLenMax = 16 * 1024 51 | SendBufLenMax = 32 * 1024 52 | ) 53 | 54 | var ( 55 | Conns ActiveConns 56 | FileHandler filehandler.FileHandler 57 | ) 58 | 59 | func Run(port int, filepath string) { 60 | listener, err := net.Listen("tcp", "127.0.0.1:"+IntToStr(port)) 61 | CheckError(err) 62 | defer listener.Close() 63 | 64 | log.Println("=====文件下载服务器已启动=====") 65 | 66 | FileHandler.Initialize(filepath) 67 | Conns.Initialize() 68 | for { 69 | conn, err := listener.Accept() 70 | if err != nil { 71 | log.Printf("Error accepting: %s\n", err.Error()) 72 | continue 73 | } 74 | go handleConn(conn) 75 | } 76 | } 77 | 78 | func handleConn(conn net.Conn) { 79 | Conns.Add(conn.RemoteAddr().String(), conn) 80 | log.Printf("当前连接数:%d\n", Conns.Count()) 81 | 82 | var msg Msg 83 | var recvBuf []byte 84 | recvBufLen := 0 85 | buf := make([]byte, MsgSize) 86 | for { 87 | count, err := conn.Read(buf) 88 | if err != nil { 89 | Conns.Remove(conn.RemoteAddr().String()) 90 | conn.Close() 91 | log.Println("连接断开:", err.Error()) 92 | log.Printf("[handleConn] 当前连接数:%d\n", Conns.Count()) 93 | break 94 | } 95 | 96 | if count+recvBufLen > RecvBufLenMax { 97 | continue 98 | } 99 | 100 | recvBuf = append(recvBuf, buf[0:count]...) 101 | recvBufLen += count 102 | offsize := 0 103 | offset := 0 104 | for recvBufLen-offsize >= MsgSize { 105 | offset = 0 106 | msg.Signature = uint32(uint32(recvBuf[offsize+3])<<24 | uint32(recvBuf[offsize+2])<<16 | uint32(recvBuf[offsize+1])<<8 | uint32(recvBuf[offsize+0])) 107 | offset += 4 108 | msg.Cmd = uint16(uint16(recvBuf[offsize+offset+1])<<8 | uint16(recvBuf[offsize+offset+0])) 109 | offset += 2 110 | msg.Param = int16(int16(recvBuf[offsize+offset+1])<<8 | int16(recvBuf[offsize+offset+0])) 111 | offset += 2 112 | copy(msg.FileName[:], recvBuf[offsize+offset+0:offsize+offset+MaxFileNameLen]) 113 | offset += MaxFileNameLen 114 | msg.Len = int32(int32(recvBuf[offsize+offset+3])<<24 | int32(recvBuf[offsize+offset+2])<<16 | int32(recvBuf[offsize+offset+1])<<8 | int32(recvBuf[offsize+offset+0])) 115 | offset += 4 116 | if msg.Signature == CustomSignature { 117 | pkglen := int(MsgSize + msg.Len) 118 | if pkglen >= RecvBufLenMax { 119 | offsize = recvBufLen 120 | break 121 | } 122 | if offsize+pkglen > recvBufLen { 123 | break 124 | } 125 | 126 | switch msg.Cmd { 127 | case CM_PING: 128 | fmt.Printf("From %s received CM_PING\n", conn.RemoteAddr().String()) 129 | reponsePing(conn) 130 | case CM_GETFILE: 131 | fmt.Printf("From %s received CM_GETFILE\n", conn.RemoteAddr().String()) 132 | responseDownloadFile( /*string(msg.FileName[:])*/ msg.FileName, conn) 133 | default: 134 | fmt.Printf("From %s received %d\n", conn.RemoteAddr().String(), msg.Cmd) 135 | } 136 | 137 | offsize += pkglen 138 | } else { 139 | offsize++ 140 | fmt.Printf("From %s received %d\n", conn.RemoteAddr().String(), msg.Cmd) 141 | } 142 | } 143 | 144 | recvBufLen -= offsize 145 | if recvBufLen > 0 { 146 | recvBuf = recvBuf[offsize : offsize+recvBufLen] 147 | } else { 148 | recvBuf = nil 149 | } 150 | } 151 | 152 | conn.Close() 153 | } 154 | 155 | func reponsePing(conn net.Conn) { 156 | var msg Msg 157 | msg.Signature = CustomSignature 158 | msg.Cmd = SM_PING 159 | msg.Param = 0 160 | msg.FileName = [MaxFileNameLen]byte{0} 161 | msg.Len = 0 162 | conn.Write(msg.Bytes()) 163 | } 164 | 165 | func responseDownloadFile(filename [MaxFileNameLen]byte, conn net.Conn) { 166 | var msg Msg 167 | msg.Signature = CustomSignature 168 | msg.Cmd = SM_GETFILE 169 | msg.FileName = filename 170 | var buf bytes.Buffer 171 | if data, err := FileHandler.GetFile(BytesToStr(filename[:])); err == nil { 172 | msg.Param = 0 173 | msg.Len = int32(len(data)) 174 | buf.Write(msg.Bytes()) 175 | buf.Write(data) 176 | } else { 177 | log.Println(err.Error()) 178 | msg.Param = -1 179 | msg.Len = 0 180 | buf.Write(msg.Bytes()) 181 | } 182 | 183 | if _, err := conn.Write(buf.Bytes()); err != nil { 184 | log.Printf("Write to %s failed: %s", conn.RemoteAddr().String(), err.Error()) 185 | } 186 | } 187 | --------------------------------------------------------------------------------