diff --git a/config.ini b/config.ini index 3d2e007..3191647 100644 --- a/config.ini +++ b/config.ini @@ -49,4 +49,9 @@ addr = 0.0.0.0:23 # Telnet 服务端地址 注意端 [ftp] status = 0 # 是否启动 Ftp 1 启动 0 关闭 -addr = 0.0.0.0:21 # Ftp 服务端地址 注意端口冲突 \ No newline at end of file +addr = 0.0.0.0:21 # Ftp 服务端地址 注意端口冲突 + +[memcache] +status = 0 # 是否启动 Memcache 1 启动 0 关闭 +addr = 0.0.0.0:11211 # Memcache 服务端地址 注意端口冲突 +ratelimit = 4 # 每秒响应次数 \ No newline at end of file diff --git a/core/protocol/memcache/LinkedHashMap/linked_hashmap.go b/core/protocol/memcache/LinkedHashMap/linked_hashmap.go new file mode 100644 index 0000000..7632986 --- /dev/null +++ b/core/protocol/memcache/LinkedHashMap/linked_hashmap.go @@ -0,0 +1,86 @@ +package LinkedHashMap + +import ( + "sync" +) + +type LinkedHashMapNode struct { + linklistNode *LinkListNode + val interface{} +} + +type LinkedHashMap struct { + linklist *LinkList + hashmap map[string]interface{} + mutex *sync.RWMutex +} + +func NewLinkedHashMap() *LinkedHashMap { + return &LinkedHashMap{ + linklist: NewLinkList(), + hashmap: make(map[string]interface{}), + mutex: &sync.RWMutex{}, + } +} + +func (this *LinkedHashMap) Lock() { + this.mutex.Lock() +} + +func (this *LinkedHashMap) Unlock() { + this.mutex.Unlock() +} + +func (this *LinkedHashMap) RLock() { + this.mutex.RLock() +} + +func (this *LinkedHashMap) RUnlock() { + this.mutex.RUnlock() +} + +func (this *LinkedHashMap) Add(key string, val interface{}) bool { + _, isExists := this.hashmap[key] + if isExists { + return false + } + + linkListNode := this.linklist.AddToTail(key) + this.hashmap[key] = &LinkedHashMapNode{ + linklistNode: linkListNode, + val: val, + } + + return true +} + +func (this *LinkedHashMap) Get(key string) interface{} { + originLinkedHashMapNode, isExists := this.hashmap[key] + if !isExists { + return nil + } + + return (originLinkedHashMapNode.(*LinkedHashMapNode)).val +} + +func (this *LinkedHashMap) Len() int { + return len(this.hashmap) +} + + +func (this *LinkedHashMap) Remove(key string) (bool, interface{}) { + originLinkedHashMapNode, isExists := this.hashmap[key] + if !isExists { + return false, nil + } + + linkedHashMapNode := originLinkedHashMapNode.(*LinkedHashMapNode) + + delete(this.hashmap, key) + this.linklist.RemoveNode(linkedHashMapNode.linklistNode) + return true, linkedHashMapNode.val +} + +func (this *LinkedHashMap) GetLinkList() *LinkList { + return this.linklist +} \ No newline at end of file diff --git a/core/protocol/memcache/LinkedHashMap/linklist.go b/core/protocol/memcache/LinkedHashMap/linklist.go new file mode 100644 index 0000000..cba7373 --- /dev/null +++ b/core/protocol/memcache/LinkedHashMap/linklist.go @@ -0,0 +1,141 @@ +package LinkedHashMap + +type LinkListNode struct { + last *LinkListNode + next *LinkListNode + val interface{} +} + +func NewLinkListNode(last *LinkListNode, next *LinkListNode, val interface{}) *LinkListNode { + node := &LinkListNode{ + last: last, + next: next, + val: val, + } + return node +} + +func (this *LinkListNode) SetLast(node *LinkListNode) { + this.last = node +} + +func (this *LinkListNode) SetNext(node *LinkListNode) { + this.next = node +} + +func (this *LinkListNode) GetLast() *LinkListNode { + return this.last +} + +func (this *LinkListNode) GetNext() *LinkListNode { + return this.next +} + +func (this *LinkListNode) GetVal() interface{} { + return this.val +} + +func (this *LinkListNode) IsHead() bool { + return this.last == nil +} + +func (this *LinkListNode) IsTail() bool { + return this.next == nil +} + +type LinkList struct { + head *LinkListNode + tail *LinkListNode + length int +} + +func NewLinkList() *LinkList { + return &LinkList{ + head: nil, + tail: nil, + length: 0, + } +} + +func (this *LinkList) GetHead() *LinkListNode { + return this.head +} + +func (this *LinkList) GetTail() *LinkListNode { + return this.tail +} + +func (this *LinkList) AddToHead(val interface{}) *LinkListNode { + if this.head == nil && this.tail == nil { + return this.addFirstNode(val) + + } + node := NewLinkListNode(nil, this.head, val) + this.head.SetLast(node) + this.head = node + this.length++ + return node +} + +func (this *LinkList) AddToTail(val interface{}) *LinkListNode { + if this.head == nil && this.tail == nil { + return this.addFirstNode(val) + + } + node := NewLinkListNode(this.tail, nil, val) + this.tail.SetNext(node) + this.tail = node + this.length++ + return node +} + +func (this *LinkList) RemoveNode(node *LinkListNode) { + defer func() { + this.length-- + }() + + /* LinkList中只有1个元素 */ + if node.IsHead() && node.IsTail() { + this.head = nil + this.tail = nil + return + } + + /* 节点是头节点 */ + if node.IsHead() { + nextNode := node.GetNext() + this.head = nextNode + nextNode.SetLast(nil) + node.SetNext(nil) + return + } + + /* 节点是尾节点 */ + if node.IsTail() { + lastNode := node.GetLast() + this.tail = lastNode + lastNode.SetNext(nil) + node.SetLast(nil) + return + } + + lastNode := node.GetLast() + nextNode := node.GetNext() + + lastNode.SetNext(nextNode) + nextNode.SetLast(lastNode) + node.SetLast(nil) + node.SetNext(nil) +} + +func (this *LinkList) GetLength() int { + return this.length +} + +func (this *LinkList) addFirstNode(val interface{}) *LinkListNode { + node := NewLinkListNode(nil, nil, val) + this.head = node + this.tail = node + this.length++ + return node +} diff --git a/core/protocol/memcache/global.go b/core/protocol/memcache/global.go new file mode 100644 index 0000000..8dea4af --- /dev/null +++ b/core/protocol/memcache/global.go @@ -0,0 +1,23 @@ +package memcache + +import ( + "math/rand" + "time" +) + +func randNumber(min int, max int) int { + return rand.Intn(max-min) + min +} + +var UPTIME time.Time = time.Now() +var RESPONSE_OK []byte = []byte("OK\r\n") +var RESPONSE_END []byte = []byte("END\r\n") +var RESPONSE_RESET []byte = []byte("RESET\r\n") +var RESPONSE_ERROR []byte = []byte("ERROR\r\n") +var RESPONSE_DELETED []byte = []byte("DELETED\r\n") +var RESPONSE_NOT_FOUND []byte = []byte("NOT_FOUND\r\n") +var RESPONSE_VERSION []byte = []byte("VERSION 1.5.16\r\n") +var RESPONSE_CLIENT_ERROR []byte = []byte("CLIENT_ERROR ") +var RESPONSE_SERVER_ERROR []byte = []byte("SERVER_ERROR ") +var RESPONSE_BAD_COMMAND_LINE []byte = []byte("bad command line format\r\n") +var RESPONSE_OBJECT_TOO_LARGE []byte = []byte("object too large for cache\r\n") diff --git a/core/protocol/memcache/memcache.go b/core/protocol/memcache/memcache.go new file mode 100644 index 0000000..4b35843 --- /dev/null +++ b/core/protocol/memcache/memcache.go @@ -0,0 +1,554 @@ +package memcache + +/** + * Supported commands: + * get \r\n + * set \r\ndata\r\n + * add \r\ndata\r\n + * delete + * flush_all + * stats + * stats slabs + * stats malloc + * stats items + * stats detail + * stats sizes + * version + * verbosity + * quit + */ + +import ( + "HFish/core/protocol/memcache/LinkedHashMap" + "bufio" + "fmt" + "io" + "log" + "net" + "os" + "strconv" + "strings" + "time" +) + +var linkedHashMap = LinkedHashMap.NewLinkedHashMap() +var networkRx = 100 +var networkTx = 150 + +var commands = map[string]func([]string) ([]byte, int){ + + "quit": func(args []string) ([]byte, int) { + return nil, -1 + }, + + "verbosity": func(args []string) ([]byte, int) { + if len(args) < 1 { + return RESPONSE_ERROR, 0 + } + return RESPONSE_OK, 0 + }, + + "flush_all": func(args []string) ([]byte, int) { + linkedHashMap.Lock() + defer linkedHashMap.Unlock() + + linklist := linkedHashMap.GetLinkList() + for { + node := linklist.GetHead() + if node == nil { + break + } + key := node.GetVal().(string) + nextNode := node.GetNext() + linkedHashMap.Remove(key) + node = nextNode + } + + return RESPONSE_OK, 0 + }, + + "version": func(args []string) ([]byte, int) { + if len(args) != 0 { + return RESPONSE_ERROR, 0 + } + return RESPONSE_VERSION, 0 + }, + + "get": func(args []string) ([]byte, int) { + if len(args) < 1 { + return RESPONSE_ERROR, 0 + } + + linkedHashMap.RLock() + defer linkedHashMap.RUnlock() + + val := linkedHashMap.Get(args[0]) + if val == nil { + return RESPONSE_END, 0 + } + return append([]byte(val.(string)+"\r\n"), RESPONSE_END...), 0 + }, + + "set": func(args []string) ([]byte, int) { + if len(args) == 5 { + key := args[0] + data := args[4] + linkedHashMap.Lock() + defer linkedHashMap.Unlock() + items := linkedHashMap.Len() + for ; items > 60; items-- { + _, _ = linkedHashMap.Remove(linkedHashMap.GetLinkList().GetHead().GetVal().(string)) + } + linkedHashMap.Add(key, data) + return []byte("STORED\r\n"), 0 + } + + if len(args) == 4 { + flags := args[1] + ttl := args[2] + size := args[3] + + _, err := strconv.Atoi(flags) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + _, err = strconv.Atoi(ttl) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + parsedSize, err := strconv.Atoi(size) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + if parsedSize > 1500 { + return append(RESPONSE_SERVER_ERROR, RESPONSE_OBJECT_TOO_LARGE...), 0 + } + return nil, parsedSize + } + + return RESPONSE_ERROR, 0 + }, + + "add": func(args []string) ([]byte, int) { + if len(args) == 5 { + key := args[0] + data := args[4] + linkedHashMap.Lock() + defer linkedHashMap.Unlock() + if linkedHashMap.Get(key) != nil { + return []byte("NOT_STORED\r\n"), 0 + } + items := linkedHashMap.Len() + for ; items > 60; items-- { + fmt.Println(items) + _, _ = linkedHashMap.Remove(linkedHashMap.GetLinkList().GetHead().GetVal().(string)) + } + linkedHashMap.Add(key, data) + return []byte("STORED\r\n"), 0 + } + + if len(args) == 4 { + flags := args[1] + ttl := args[2] + size := args[3] + + _, err := strconv.Atoi(flags) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + _, err = strconv.Atoi(ttl) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + parsedSize, err := strconv.Atoi(size) + if err != nil { + return append(RESPONSE_CLIENT_ERROR, RESPONSE_BAD_COMMAND_LINE...), 0 + } + if parsedSize > 1024 { + return append(RESPONSE_SERVER_ERROR, RESPONSE_OBJECT_TOO_LARGE...), 0 + } + return nil, parsedSize + } + + return RESPONSE_ERROR, 0 + }, + + "delete": func(args []string) ([]byte, int) { + if len(args) < 1 { + return RESPONSE_ERROR, 0 + } + + linkedHashMap.Lock() + defer linkedHashMap.Unlock() + + result, _ := linkedHashMap.Remove(args[0]) + if !result { + return RESPONSE_NOT_FOUND, 0 + } + return RESPONSE_DELETED, 0 + }, + + "stats": func(args []string) ([]byte, int) { + nowTime := time.Now() + networkRx = networkRx + randNumber(10, 50) + networkTx = networkTx + randNumber(100, 500) + linkedHashMap.RLock() + defer linkedHashMap.RUnlock() + items := linkedHashMap.Len() + + if len(args) == 0 { + statsArray := []string{ + "STAT pid 1\r\n", + fmt.Sprintf("STAT uptime %d\r\n", int(nowTime.Sub(UPTIME)/time.Second)), + fmt.Sprintf("STAT time %d\r\n", nowTime.Unix()), + "STAT version 1.5.16\r\n", + "STAT libevent 2.1.8-stable\r\n", + "STAT pointer_size 64\r\n", + "STAT rusage_user 0.029000\r\n", + "STAT rusage_system 0.029000\r\n", + "STAT max_connections 1024\r\n", + fmt.Sprintf("STAT curr_connections %d\r\n", randNumber(1, 5)), + "STAT rejected_connections 0\r\n", + "STAT connection_structures 3\r\n", + "STAT reserved_fds 20\r\n", + "STAT cmd_get 0\r\n", + "STAT cmd_set 0\r\n", + "STAT cmd_flush 0\r\n", + "STAT cmd_touch 0\r\n", + "STAT get_hits 0\r\n", + "STAT get_misses 0\r\n", + "STAT get_expired 0\r\n", + "STAT get_flushed 0\r\n", + "STAT delete_misses 0\r\n", + "STAT delete_hits 0\r\n", + "STAT incr_misses 0\r\n", + "STAT incr_hits 0\r\n", + "STAT decr_misses 0\r\n", + "STAT decr_hits 0\r\n", + "STAT cas_misses 0\r\n", + "STAT cas_hits 0\r\n", + "STAT cas_badval 0\r\n", + "STAT touch_hits 0\r\n", + "STAT touch_misses 0\r\n", + "STAT auth_cmds 0\r\n", + "STAT auth_errors 0\r\n", + fmt.Sprintf("STAT bytes_read %d\r\n", networkRx), + fmt.Sprintf("STAT bytes_written %d\r\n", networkTx), + "STAT limit_maxbytes 67108864\r\n", + "STAT accepting_conns 1\r\n", + "STAT listen_disabled_num 0\r\n", + "STAT time_in_listen_disabled_us 0\r\n", + "STAT threads 4\r\n", + "STAT conn_yields 0\r\n", + "STAT hash_power_level 16\r\n", + "STAT hash_bytes 524288\r\n", + "STAT hash_is_expanding 0\r\n", + "STAT slab_reassign_rescues 0\r\n", + "STAT slab_reassign_chunk_rescues 0\r\n", + "STAT slab_reassign_evictions_nomem 0\r\n", + "STAT slab_reassign_inline_reclaim 0\r\n", + "STAT slab_reassign_busy_items 0\r\n", + "STAT slab_reassign_busy_deletes 0\r\n", + "STAT slab_reassign_running 0\r\n", + "STAT slabs_moved 0\r\n", + "STAT lru_crawler_running 0\r\n", + "STAT lru_crawler_starts 510\r\n", + "STAT lru_maintainer_juggles 224\r\n", + "STAT malloc_fails 0\r\n", + "STAT log_worker_dropped 0\r\n", + "STAT log_worker_written 0\r\n", + "STAT log_watcher_skipped 0\r\n", + "STAT log_watcher_sent 0\r\n", + "STAT bytes 0\r\n", + fmt.Sprintf("STAT curr_items %d\r\n", items), + "STAT total_items 0\r\n", + "STAT slab_global_page_pool 0\r\n", + "STAT expired_unfetched 0\r\n", + "STAT evicted_unfetched 0\r\n", + "STAT evicted_active 0\r\n", + "STAT evictions 0\r\n", + "STAT reclaimed 0\r\n", + "STAT crawler_reclaimed 0\r\n", + "STAT crawler_items_checked 0\r\n", + "STAT lrutail_reflocked 0\r\n", + "STAT moves_to_cold 0\r\n", + "STAT moves_to_warm 0\r\n", + "STAT moves_within_lru 0\r\n", + "STAT direct_reclaims 0\r\n", + "STAT lru_bumps_dropped 0\r\n", + "END\r\n", + } + responseBuffer := []byte{} + for _, response := range statsArray { + responseBuffer = append(responseBuffer, []byte(response)...) + } + return responseBuffer, 0 + } + + switch args[0] { + case "slabs": + statsArray := []string{ + "STAT 1:chunk_size 96\r\n", + "STAT 1:chunks_per_page 10922\r\n", + "STAT 1:total_pages 1\r\n", + "STAT 1:total_chunks 10922\r\n", + "STAT 1:used_chunks 1\r\n", + "STAT 1:free_chunks 10921\r\n", + "STAT 1:free_chunks_end 0\r\n", + "STAT 1:mem_requested 68\r\n", + "STAT 1:get_hits 0\r\n", + "STAT 1:cmd_set 0\r\n", + "STAT 1:delete_hits 0\r\n", + "STAT 1:incr_hits 0\r\n", + "STAT 1:decr_hits 0\r\n", + "STAT 1:cas_hits 0\r\n", + "STAT 1:cas_badval 0\r\n", + "STAT 1:touch_hits 0\r\n", + "STAT 2:chunk_size 120\r\n", + "STAT 2:chunks_per_page 8738\r\n", + "STAT 2:total_pages 1\r\n", + "STAT 2:total_chunks 8738\r\n", + "STAT 2:used_chunks 0\r\n", + "STAT 2:free_chunks 8738\r\n", + "STAT 2:free_chunks_end 0\r\n", + "STAT 2:mem_requested 0\r\n", + "STAT 2:get_hits 0\r\n", + "STAT 2:cmd_set 0\r\n", + "STAT 2:delete_hits 0\r\n", + "STAT 2:incr_hits 0\r\n", + "STAT 2:decr_hits 0\r\n", + "STAT 2:cas_hits 0\r\n", + "STAT 2:cas_badval 0\r\n", + "STAT 2:touch_hits 0\r\n", + "STAT 39:chunk_size 524288\r\n", + "STAT 39:chunks_per_page 2\r\n", + "STAT 39:total_pages 1\r\n", + "STAT 39:total_chunks 2\r\n", + "STAT 39:used_chunks 0\r\n", + "STAT 39:free_chunks 2\r\n", + "STAT 39:free_chunks_end 0\r\n", + "STAT 39:mem_requested 0\r\n", + "STAT 39:get_hits 0\r\n", + "STAT 39:cmd_set 0\r\n", + "STAT 39:delete_hits 0\r\n", + "STAT 39:incr_hits 0\r\n", + "STAT 39:decr_hits 0\r\n", + "STAT 39:cas_hits 0\r\n", + "STAT 39:cas_badval 0\r\n", + "STAT 39:touch_hits 0\r\n", + "STAT active_slabs 3\r\n", + "STAT total_malloced 3145728\r\n", + "END\r\n", + } + responseBuffer := []byte{} + for _, response := range statsArray { + responseBuffer = append(responseBuffer, []byte(response)...) + } + return responseBuffer, 0 + case "items": + statsArray := []string{ + fmt.Sprintf("STAT items:1:number %d\r\n", items), + "STAT items:1:number_hot 0\r\n", + "STAT items:1:number_warm 0\r\n", + fmt.Sprintf("STAT items:1:number_cold %d\r\n", items), + "STAT items:1:age_hot 0\r\n", + "STAT items:1:age_warm 0\r\n", + "STAT items:1:age 31\r\n", + "STAT items:1:evicted 0\r\n", + "STAT items:1:evicted_nonzero 0\r\n", + "STAT items:1:evicted_time 0\r\n", + "STAT items:1:outofmemory 0\r\n", + "STAT items:1:tailrepairs 0\r\n", + "STAT items:1:reclaimed 1\r\n", + "STAT items:1:expired_unfetched 1", + "STAT items:1:evicted_unfetched 0\r\n", + "STAT items:1:evicted_active 0\r\n", + "STAT items:1:crawler_reclaimed 0\r\n", + "STAT items:1:crawler_items_checked 0\r\n", + "STAT items:1:lrutail_reflocked 0\r\n", + "STAT items:1:moves_to_cold 3\r\n", + "STAT items:1:moves_to_warm 0\r\n", + "STAT items:1:moves_within_lru 0\r\n", + "STAT items:1:direct_reclaims 0\r\n", + "STAT items:1:hits_to_hot 0\r\n", + "STAT items:1:hits_to_warm 0\r\n", + "STAT items:1:hits_to_cold 0\r\n", + "STAT items:1:hits_to_temp 0\r\n", + "END\r\n", + } + responseBuffer := []byte{} + for _, response := range statsArray { + responseBuffer = append(responseBuffer, []byte(response)...) + } + return responseBuffer, 0 + case "detail": + return RESPONSE_OK, 0 + case "sizes": + return append([]byte("STAT sizes_status disabled\r\n"), RESPONSE_END...), 0 + case "reset": + return RESPONSE_RESET, 0 + default: + break + } + + return RESPONSE_ERROR, 0 + }, +} + +// TCP服务端连接 +func tcpServer(address string, rateLimitChan chan int, exitChan chan int) { + l, err := net.Listen("tcp", address) + + if err != nil { + fmt.Println(err.Error()) + exitChan <- 1 + } + + log.Println("[Memcache TCP] Listning on " + address) + + defer l.Close() + + for { + conn, err := l.Accept() + trackID := randNumber(100000, 999999) + + if err != nil { + fmt.Println(err.Error()) + continue + } + + go func() { + skip := false + reader := bufio.NewReader(conn) + log.Printf("[Memcache TCP %d] Accepted a client socket from %s\n", trackID, conn.RemoteAddr().String()) + for { + <-rateLimitChan + str, err := reader.ReadString('\n') + if skip { + skip = false + continue + } + if err != nil { + log.Printf("[Memcache TCP %d] Closed a client socket.\n", trackID) + conn.Close() + break + } + str = strings.TrimSpace(str) + + log.Printf("[Memcache TCP %d] Client request: %s.\n", trackID, str) + args := strings.Split(str, " ") + function, exist := commands[args[0]] + if !exist { + conn.Write(RESPONSE_ERROR) + continue + } + + args = args[1:] + + for { + response, requiredBytes := function(args) + if requiredBytes == -1 { + conn.Close() + break + } + if requiredBytes == 0 { + conn.Write(response) + break + } + + data := make([]byte, requiredBytes) + _, err = io.ReadFull(reader, data) + if err != nil { + break + } + skip = true + args = append(args, string(data)) + } + + } + }() + } + +} + +func udpServer(address string, rateLimitChan chan int, exitChan chan int) { + udpAddr, err := net.ResolveUDPAddr("udp", address) + if err != nil { + fmt.Println(err.Error()) + exitChan <- 1 + } + + l, err := net.ListenUDP("udp", udpAddr) + if err != nil { + fmt.Println(err.Error()) + exitChan <- 1 + } + + log.Println("[Memcache UDP] Listning on " + address) + + go func() { + buf := make([]byte, 1500) + for { + <-rateLimitChan + plen, addr, _ := l.ReadFromUDP(buf) + /* UDP协议需要8个字节的头 */ + if plen < 8 { + continue + } + requestStr := string(buf[8:plen]) + + for _, request := range strings.Split(requestStr, "\n") { + request = strings.TrimSpace(request) + if request == "" { + continue + } + log.Printf("[Memcache UDP] Client request: [%s] from: %s\n", request, addr.String()) + args := strings.Split(request, " ") + function, exist := commands[args[0]] + if !exist { + continue + } + args = args[1:] + + response, requiredBytes := function(args) + if requiredBytes != 0 { + continue + } + if len(response) > 1300 { + response = response[:1300] + } + l.WriteTo(append([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, response...), addr) + } + + } + }() +} + +func Start(addr string, rateLimitStr string) { + // 创建一个程序结束码的通道 + exitChan := make(chan int) + + // 响应间隔限制 + rateLimitChan := make(chan int) + rateLimit, err := strconv.Atoi(rateLimitStr) + if err != nil { + panic(err) + } + go func() { + sleepTime := 1000 / rateLimit + for { + rateLimitChan <- 1 + time.Sleep(time.Duration(sleepTime) * time.Millisecond) + } + }() + + // 将服务器并发运行 + go tcpServer(addr, rateLimitChan, exitChan) + go udpServer(addr, rateLimitChan, exitChan) + + // 通道阻塞,等待接受返回值 + code := <-exitChan + + // 标记程序返回值并退出 + os.Exit(code) +} diff --git a/utils/setting/setting.go b/utils/setting/setting.go index 27a1616..da3611b 100644 --- a/utils/setting/setting.go +++ b/utils/setting/setting.go @@ -14,6 +14,7 @@ import ( "HFish/core/protocol/ssh" "HFish/core/protocol/redis" "HFish/core/protocol/mysql" + "HFish/core/protocol/memcache" "HFish/core/protocol/ftp" "HFish/core/protocol/telnet" "HFish/core/rpc/server" @@ -208,6 +209,18 @@ func Run() { //=========================// + // 启动 Memcache 蜜罐 + memcacheStatus := conf.Get("memcache", "status") + memcacheRateLimit := conf.Get("memcache", "ratelimit") + + // 判断 暗网 Web 蜜罐 是否开启 + if memcacheStatus == "1" { + memcacheAddr := conf.Get("memcache", "addr") + go memcache.Start(memcacheAddr, memcacheRateLimit) + } + + //=========================// + // 启动 RPC rpcStatus := conf.Get("rpc", "status")