種子釋出和bt檔案分發系統
基於github開源專案Taipei-Torrent
btmaster
主要監聽61111埠,可以製作種子和傳送種子檔案,開啟tracker,開啟原始下載BT
btslave
向btmaster報告狀態,下載種子檔案,根據種子下載bt分發檔案
btmaster原始碼:
package main
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/jackpal/Taipei-Torrent/torrent"
"github.com/jackpal/Taipei-Torrent/tracker"
"golang.org/x/net/proxy"
"io"
"io/ioutil"
"log"
"math"
"net"
"os"
"os/signal"
"path"
"protocol"
)
//包括tracker,bt和種子及命令下發
//1. 製作檔案種子
//2. 啟動tracker
//3. 啟動上傳bt
//4. 向slave下發種子
//5. 向slave下發開始下載命令
//6. 統計slave下載完成情況
//7. 終止bt下載任務
//製作檔案種子
func Make_Torrent(Filename string, Tracker string) (string, string, string) {
torrentFileN := Filename + ".torrent"
createTorrent := Filename
createTracker := Tracker
torrentFile, err := os.OpenFile(torrentFileN, os.O_RDWR|os.O_CREATE, 0)
if err != nil {
log.Fatalln("Failed to open file!", err)
}
defer torrentFile.Close()
wterr := torrent.WriteMetaInfoBytes(createTorrent, createTracker, torrentFile)
if wterr != nil {
log.Fatal("Could not create torrent file:", wterr)
}
//計算torrent檔案base64編碼
torrentbytes, _ := ioutil.ReadFile(torrentFileN)
torrentbase64 := base64.StdEncoding.EncodeToString(torrentbytes)
//計算torrent檔案md5
filemd5, _ := GetFileMd5(torrentFileN)
return torrentFileN, torrentbase64, filemd5
}
//檔案md5值計算
func GetFileMd5(filename string) (string, error) {
file, err := os.Open(filename)
if err != nil {
fmt.Println("os Open error")
return "", err
}
md5 := md5.New()
_, err = io.Copy(md5, file)
if err != nil {
fmt.Println("io copy error")
return "", err
}
md5Str := hex.EncodeToString(md5.Sum(nil))
return md5Str, nil
}
//啟動tracker
func Start_Tracker(addr string, torrentFiles []string) (err error) {
t := tracker.NewTracker()
// TODO(jackpal) Allow caller to choose port number
t.Addr = addr
dial := proxy.FromEnvironment()
for _, torrentFile := range torrentFiles {
var metaInfo *torrent.MetaInfo
metaInfo, err = torrent.GetMetaInfo(dial, torrentFile)
if err != nil {
return
}
name := metaInfo.Info.Name
if name == "" {
name = path.Base(torrentFile)
}
err = t.Register(metaInfo.InfoHash, name)
if err != nil {
return
}
}
go func() {
quitChan := listenSigInt()
select {
case <-quitChan:
log.Printf("got control-C")
t.Quit()
}
}()
err = t.ListenAndServe()
if err != nil {
return
}
return
}
//啟動上傳bt
var (
cpuprofile = "" //If not empty, collects CPU profile samples and writes the profile to the given file before the program exits
memprofile = "" //If not empty, writes memory heap allocations to the given file before the program exits
createTorrent = "" //If not empty, creates a torrent file from the given root. Writes to stdout
createTracker = "" //Creates a tracker serving the given torrent file on the given address. Example --createTracker=:8080 to serve on port 8080.
port = 7777 //Port to listen on. 0 means pick random port. Note that 6881 is blacklisted by some trackers.
fileDir = "." //path to directory where files are stored
seedRatio = math.Inf(0) //Seed until ratio >= this value before quitting.
useDeadlockDetector = false //Panic and print stack dumps when the program is stuck.
useLPD = false //Use Local Peer Discovery
useUPnP = false //Use UPnP to open port in firewall.
useNATPMP = false //Use NAT-PMP to open port in firewall.
gateway = "" //IP Address of gateway.
useDHT = false //Use DHT to get peers.
trackerlessMode = false //Do not get peers from the tracker. Good for testing DHT mode.
proxyAddress = "" //Address of a SOCKS5 proxy to use.
initialCheck = true //Do an initial hash check on files when adding torrents.
useSFTP = "" //SFTP connection string, to store torrents over SFTP. e.g. 'username:password@192.168.1.25:22/path/'
useRamCache = 0 //Size in MiB of cache in ram, to reduce traffic on torrent storage.
useHdCache = 0 //Size in MiB of cache in OS temp directory, to reduce traffic on torrent storage.
execOnSeeding = "" //Command to execute when torrent has fully downloaded and has begun seeding.
quickResume = false //Save torrenting data to resume faster. '-initialCheck' should be set to false, to prevent hash check on resume.
maxActive = 16 //How many torrents should be active at a time. Torrents added beyond this value are queued.
memoryPerTorrent = -1 //Maximum memory (in MiB) per torrent used for Active Pieces. 0 means minimum. -1 (default) means unlimited.
torrentFiles []string
)
func parseTorrentFlags() (flags *torrent.TorrentFlags, err error) {
dialer := proxy.FromEnvironment()
flags = &torrent.TorrentFlags{
Dial: dialer,
Port: port,
FileDir: fileDir,
SeedRatio: seedRatio,
UseDeadlockDetector: useDeadlockDetector,
UseLPD: useLPD,
UseDHT: useDHT,
UseUPnP: useUPnP,
UseNATPMP: useNATPMP,
TrackerlessMode: trackerlessMode,
// IP address of gateway
Gateway: gateway,
InitialCheck: initialCheck,
FileSystemProvider: torrent.OsFsProvider{},
Cacher: nil,
ExecOnSeeding: execOnSeeding,
QuickResume: quickResume,
MaxActive: maxActive,
MemoryPerTorrent: memoryPerTorrent,
}
return
}
func Start_BT(torrentFile string) {
torrentFiles = []string{torrentFile}
torrentFlags, err := parseTorrentFlags()
if err != nil {
log.Fatal("Could not parse flags:", err)
}
log.Println("Starting.")
err = torrent.RunTorrents(torrentFlags, torrentFiles)
if err != nil {
log.Fatal("Could not run torrents", err)
}
}
//向slave下發種子
func Distribute_Torrent() {
}
//向slave下發開始下載命令
func Start_Btslave() {
}
//統計slave下載完成情況
func Mission_Status() {
}
//終止bt下載任務
func Stop_Btslave() {
}
//功能函式
//判斷檔案是否存在
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
//手動中斷
func listenSigInt() chan os.Signal {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
return c
}
//Socket通訊函式
//下發任務
func handleConnection_SendMission(conn net.Conn, mission string) {
//sendmission := "{\"Mission\":\"" + mission + "\", \"TorrentFile\":\"" + torrentfile + "\", \"TorrentContent\":\"" + torrentcontent + "\", \"TorrentMD5\": \"" + torrentMD5 + "\"}"
//startbt := "{\"Mission\":\"StartBT\", \"TorrentFile\":\"test.torrent\", \"TorrentContent\":\"\"}"
//stopbt := "{\"Mission\":\"StopBT\", \"TorrentFile\":\"test.torrent\", \"TorrentContent\":\"\"}"
conn.Write(protocol.Enpack([]byte(mission)))
//conn.Write(protocol.Enpack([]byte(startbt)))
//conn.Write(protocol.Enpack([]byte(stopbt)))
Log(mission)
Log("Mission sent.")
//defer conn.Close()
}
func handleConnection_getStatus(conn net.Conn) {
// 緩衝區,儲存被截斷的資料
tmpBuffer := make([]byte, 0)
//接收解包
readerChannel := make(chan []byte, 16)
go reader(readerChannel, conn)
buffer := make([]byte, 1024)
for {
n, err := conn.Read(buffer)
if err != nil {
if err == io.EOF {
Log("Client disconnected.")
} else {
Log(conn.RemoteAddr().String(), " connection error: ", err)
}
return
}
tmpBuffer = protocol.Depack(append(tmpBuffer, buffer[:n]...), readerChannel)
}
//defer conn.Close()
}
//讀取channel中的訊息並作出相應的操作和回應
func reader(readerChannel chan []byte, conn net.Conn) {
for {
select {
case data := <-readerChannel:
var dat map[string]interface{}
if err := json.Unmarshal([]byte(string(data)), &dat); err == nil {
if dat["Mission"].(string) == "DownloadTorrent" {
status := dat["Status"].(string)
Log("DownloadTorrent Mission Status: " + status)
torrentfile := "Rise.of.the.Tomb.Raider.CN.HD.part01.rar.torrent"
if status == "OK" {
// mission := "{\"Mission\":\"StartBT\", \"TorrentFile\":\"" + dat["TorrentFile"].(string) + "\", \"TorrentContent\":\"\", \"TorrentMD5\": \"\"}"
// Log(mission)
handleConnection_SendMission(conn, "{\"Mission\":\"StartBT\", \"TorrentFile\":\"" + torrentfile + "\", \"TorrentContent\":\"\", \"TorrentMD5\": \"\"}")
}
}
if dat["Mission"].(string) == "StartBT" {
status := dat["Status"].(string)
if status == "OK" {
Log(conn.RemoteAddr().String(), "BT started.")
}
}
if dat["Mission"].(string) == "StopBT" {
}
} else {
fmt.Println(err)
}
}
}
}
func Log(v ...interface{}) {
log.Println(v...)
}
func CheckError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
//main函式
func main() {
//json mission結構體
//type Mission struct {
// mission string
// torrentfile string
// torrentcontent string
// torrentMD5 string
//}
//建立socket,監聽埠
netListen, err := net.Listen("tcp", "0.0.0.0:61111")
CheckError(err)
defer netListen.Close()
Log("Waiting for clients")
tracker := "127.0.0.1:6969"
dir := ""
file := dir + "Rise.of.the.Tomb.Raider.CN.HD.part01.rar"
torrent := file + ".torrent"
hastorrent, _ := PathExists(torrent)
if !hastorrent {
torrentfile, torrentcontent, torrentMD5 := Make_Torrent(file, tracker)
//啟動tracker
go Start_Tracker(tracker, []string{torrentfile})
//啟動上傳bt
go Start_BT(torrentfile)
//向btslave傳送命令和檔案
for {
conn, err := netListen.Accept()
if err != nil {
continue
}
Log(conn.RemoteAddr().String(), " tcp connect success")
mission := "{\"Mission\":\"DownloadTorrent\", \"TorrentFile\":\"" + torrentfile + "\", \"TorrentContent\":\"" + torrentcontent + "\", \"TorrentMD5\": \"" + torrentMD5 + "\"}"
go handleConnection_SendMission(conn, mission)
go handleConnection_getStatus(conn)
//go handleConnection_getStatus(conn)
//if newmission {
// maketorrent
// gettorrentmd5
// base64torrent
// starttracker
// startsourcebt
// sendtorrenttoslave
// sendcommandtoslavetostartbt
//}
//if missionfinished {
// sendcommand(stopslavebt)
// stopsourcebt
// stoptracker
// reportsuccess
//}
}
} else {
os.Remove(torrent)
torrentfile, torrentcontent, torrentMD5 := Make_Torrent(file, tracker)
//啟動tracker
go Start_Tracker(tracker, []string{torrentfile})
//啟動上傳bt
go Start_BT(torrentfile)
//向btslave傳送命令和檔案
for {
conn, err := netListen.Accept()
if err != nil {
continue
}
Log(conn.RemoteAddr().String(), " tcp connect success")
mission := "{\"Mission\":\"DownloadTorrent\", \"TorrentFile\":\"" + torrentfile + "\", \"TorrentContent\":\"" + torrentcontent + "\", \"TorrentMD5\": \"" + torrentMD5 + "\"}"
go handleConnection_SendMission(conn, mission)
go handleConnection_getStatus(conn)
}
}
}
//ToDO:
//如何stopbt任務
//如何判斷何時終止bt任務
btslave原始碼
package main
import (
"encoding/json"
"fmt"
"github.com/jackpal/Taipei-Torrent/torrent"
"golang.org/x/net/proxy"
"io"
"log"
"math"
"net"
"os"
"protocol"
"encoding/base64"
"crypto/md5"
"encoding/hex"
)
//1. 接收master命令下載種子檔案
//2. 接收master命令開始bt下載
//3. 接收master命令終止bt下載
//接收master命令下載種子檔案
func Receive_Torrent() {
}
//接收master命令開始bt下載
func Start_Bt() {
}
//接收master命令終止bt下載
func Stop_Bt() {
}
//傳送任務狀態
func handleConnection_SendStatus(conn net.Conn, mission string, status string) {
sendstatus := "{\"Mission\":\"" + mission + "\", \"Status\":\"" + status + "\"}"
Log(sendstatus)
conn.Write(protocol.Enpack([]byte(sendstatus)))
Log("Status sent.")
//defer conn.Close()
}
func handleConnection_getMission(conn net.Conn) {
// 緩衝區,儲存被截斷的資料
tmpBuffer := make([]byte, 0)
//接收解包
readerChannel := make(chan []byte, 16)
go reader(readerChannel, conn)
buffer := make([]byte, 1024)
for {
n, err := conn.Read(buffer)
if err != nil {
if err == io.EOF {
Log("Client disconnected.")
} else {
Log(conn.RemoteAddr().String(), " connection error: ", err)
}
return
}
tmpBuffer = protocol.Depack(append(tmpBuffer, buffer[:n]...), readerChannel)
}
}
func Log(v ...interface{}) {
fmt.Println(v...)
}
//讀取channel中的訊息並作出相應的操作和回應
func reader(readerChannel chan []byte, conn net.Conn) {
for {
select {
case data := <-readerChannel:
Log(string(data))
var dat map[string]interface{}
if err := json.Unmarshal(data, &dat); err == nil {
if dat["Mission"].(string) == "DownloadTorrent" {
Log("Received Mission: DownloadTorrent")
mission := "DownloadTorrent"
//接收種子,寫入檔案,校驗md5
torrentFilename := dat["TorrentFile"].(string)
decodeBytes, err := base64.StdEncoding.DecodeString(dat["TorrentContent"].(string))
if err != nil {
log.Fatalln(err)
}
torrentMD5 := dat["TorrentMD5"].(string)
torrentFile, err := os.OpenFile(torrentFilename, os.O_RDWR|os.O_CREATE, 0)
if err != nil {
log.Fatalln("Failed to open file!", err)
}
defer torrentFile.Close()
torrentFile.Write(decodeBytes)
torrentFilemd5, _ := GetFileMd5(torrentFilename)
if torrentMD5 == torrentFilemd5 {
status := "OK"
Log("Torrent downloaded.")
handleConnection_SendStatus(conn, mission, status)
}
}
if dat["Mission"].(string) == "StartBT" {
go Start_BT(dat["TorrentFile"].(string))
Log("BT started.")
mission := "StartBT"
status := "OK"
handleConnection_SendStatus(conn, mission, status)
}
if dat["Mission"].(string) == "StopBT" {
}
//if dat["Mission"] == "StartBT" {
// //檢查BT啟動情況並報告狀態
// //checkbtstarted
// handleConnection_SendStatus(conn, mission, status)
//}
//if dat["Mission"] == "StopBT" {
// //檢查BT停止情況並報告狀態
// //checkbtstopped
// handleConnection_SendStatus(conn, mission, status)
//}
} else {
fmt.Println(err, "Json parse failed!")
}
}
}
}
//檔案md5值計算
func GetFileMd5(filename string) (string, error) {
file, err := os.Open(filename)
if err != nil {
fmt.Println("os Open error")
return "", err
}
md5 := md5.New()
_, err = io.Copy(md5, file)
if err != nil {
fmt.Println("io copy error")
return "", err
}
md5Str := hex.EncodeToString(md5.Sum(nil))
return md5Str, nil
}
//bt客戶端
var (
cpuprofile = "" //If not empty, collects CPU profile samples and writes the profile to the given file before the program exits
memprofile = "" //If not empty, writes memory heap allocations to the given file before the program exits
createTorrent = "" //If not empty, creates a torrent file from the given root. Writes to stdout
createTracker = "" //Creates a tracker serving the given torrent file on the given address. Example --createTracker=:8080 to serve on port 8080.
port = 7778 //Port to listen on. 0 means pick random port. Note that 6881 is blacklisted by some trackers.
fileDir = "." //path to directory where files are stored
seedRatio = math.Inf(0) //Seed until ratio >= this value before quitting.
useDeadlockDetector = false //Panic and print stack dumps when the program is stuck.
useLPD = false //Use Local Peer Discovery
useUPnP = false //Use UPnP to open port in firewall.
useNATPMP = false //Use NAT-PMP to open port in firewall.
gateway = "" //IP Address of gateway.
useDHT = false //Use DHT to get peers.
trackerlessMode = false //Do not get peers from the tracker. Good for testing DHT mode.
proxyAddress = "" //Address of a SOCKS5 proxy to use.
initialCheck = true //Do an initial hash check on files when adding torrents.
useSFTP = "" //SFTP connection string, to store torrents over SFTP. e.g. 'username:password@192.168.1.25:22/path/'
useRamCache = 0 //Size in MiB of cache in ram, to reduce traffic on torrent storage.
useHdCache = 0 //Size in MiB of cache in OS temp directory, to reduce traffic on torrent storage.
execOnSeeding = "" //Command to execute when torrent has fully downloaded and has begun seeding.
quickResume = false //Save torrenting data to resume faster. '-initialCheck' should be set to false, to prevent hash check on resume.
maxActive = 16 //How many torrents should be active at a time. Torrents added beyond this value are queued.
memoryPerTorrent = -1 //Maximum memory (in MiB) per torrent used for Active Pieces. 0 means minimum. -1 (default) means unlimited.
torrentFiles []string
)
func parseTorrentFlags() (flags *torrent.TorrentFlags, err error) {
dialer := proxy.FromEnvironment()
flags = &torrent.TorrentFlags{
Dial: dialer,
Port: port,
FileDir: fileDir,
SeedRatio: seedRatio,
UseDeadlockDetector: useDeadlockDetector,
UseLPD: useLPD,
UseDHT: useDHT,
UseUPnP: useUPnP,
UseNATPMP: useNATPMP,
TrackerlessMode: trackerlessMode,
// IP address of gateway
Gateway: gateway,
InitialCheck: initialCheck,
FileSystemProvider: torrent.OsFsProvider{},
Cacher: nil,
ExecOnSeeding: execOnSeeding,
QuickResume: quickResume,
MaxActive: maxActive,
MemoryPerTorrent: memoryPerTorrent,
}
return
}
//bt啟動函式
func Start_BT(torrentFile string) {
torrentFiles = []string{torrentFile}
torrentFlags, err := parseTorrentFlags()
if err != nil {
log.Fatal("Could not parse flags:", err)
}
log.Println("Starting.")
err = torrent.RunTorrents(torrentFlags, torrentFiles)
if err != nil {
log.Fatal("Could not run torrents", err)
}
}
//main函式
func main() {
server := "10.9.3.132:61111"
tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
fmt.Println("connect success")
handleConnection_getMission(conn)
}
自定義protocol模組原始碼
//通訊協議處理
package protocol
import (
"bytes"
"encoding/binary"
)
const (
ConstHeader = "Headers"
ConstHeaderLength = 7
ConstMLength = 4
)
//封包
func Enpack(message []byte) []byte {
return append(append([]byte(ConstHeader), IntToBytes(len(message))...), message...)
}
//解包
func Depack(buffer []byte, readerChannel chan []byte) []byte {
length := len(buffer)
var i int
for i = 0; i < length; i = i + 1 {
if length < i+ConstHeaderLength+ConstMLength {
break
}
if string(buffer[i:i+ConstHeaderLength]) == ConstHeader {
messageLength := BytesToInt(buffer[i+ConstHeaderLength : i+ConstHeaderLength+ConstMLength])
if length < i+ConstHeaderLength+ConstMLength+messageLength {
break
}
data := buffer[i+ConstHeaderLength+ConstMLength : i+ConstHeaderLength+ConstMLength+messageLength]
readerChannel <- data
}
}
if i == length {
return make([]byte, 0)
}
return buffer[i:]
}
//整形轉換成位元組
func IntToBytes(n int) []byte {
x := int32(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
return bytesBuffer.Bytes()
}
//位元組轉換成整形
func BytesToInt(b []byte) int {
bytesBuffer := bytes.NewBuffer(b)
var x int32
binary.Read(bytesBuffer, binary.BigEndian, &x)
return int(x)
}
相關文章
- BT自動檔案分發系統
- 使用BT協議構建軟體快速分發系統(可用於Linux內網快速分發檔案)協議Linux內網
- BT種子的技術原理是什麼?.torrent檔案如何理解?
- 檔案和檔案系統
- UNIX根檔案系統和附加檔案系統
- Dragonfly 基於 P2P 的檔案和映象分發系統Go
- Oracle 重要檔案系統解釋Oracle
- 檔案描述符和檔案系統
- WSL子系統檔案遷移至其他磁碟
- 分散式檔案儲存系統 fastdfs 的 Composer 包釋出!分散式AST
- 初探pinctrl子系統和GPIO子系統
- docker檔案系統分層儲存原理Docker
- linux 下檔案系統的劃分Linux
- fsutil,您可以執行多種檔案系統操作,包括查詢和設定檔案系統特性,refsutil 是用於管理和維護ReFS檔案系統的實用程式 管理ReFS檔案系統的命令列工具命令列
- 5種快速查詢容器檔案系統中檔案的方法
- sbt_client工具(Linux下的BT下載和種子製作工具)clientLinux
- Linux檔案系統-目錄和檔案管理Linux
- ubuntu莫名的 系統出現檔案系統只讀Ubuntu
- Win10系統訪問Linux子系統檔案的方法Win10Linux
- linux磁碟和檔案系統Linux
- ELF檔案的四種分類
- 阿里宣佈開源容器技術Pouch和P2P檔案分發系統“蜻蜓”阿里
- 檔案系統和檔案 API 安全性缺失指南API
- Linux系統篇-檔案系統&虛擬檔案系統Linux
- exp 分檔案大小匯出
- BitTorrent Sync 基於BT的檔案同步
- IT系統的三種分類
- Linux分割槽和檔案系統 ⑥Linux
- 04 磁碟儲存和檔案系統
- 作業系統——裝置驅動和檔案系統作業系統
- 各種檔案系統的允許的單個檔案最大大小
- 賽門鐵克確認誤刪除XP系統檔案 釋出更新補救
- 釋出nuget 如何配置專案檔案
- 檔案系統
- 微信小程式檔案預覽和下載-檔案系統微信小程式
- 利用ant編譯釋出打包jar檔案和打包api文件為rar檔案編譯JARAPI
- Linux系統日誌分為哪幾種?日誌檔案包括幾列內容?Linux
- 檔案系統(五):exFAT 檔案系統原理詳解