Golang如何在不覆盖现有值的情况下通过客户端请求填充Map

Golang如何在不覆盖现有值的情况下通过客户端请求填充Map 我是Go语言的初学者。这是一个TCP服务器,它接收请求并向客户端发送一条问候消息,该消息附带了客户端的消息(名称)。我如何将这些请求填充到一个映射(map)中,而不覆盖之前的请求? 这是代码,请求被追加到了映射中,但当下一个请求到来时,它会替换当前的那个。

// Goroutine and Socket programming TCP server
    //**************************************************
    //                 TCP 
    //----------------------------------------------------------

    package main

    import (
    	"bufio"
    	"fmt"
    	"net"
    	"time"
    )
    // check if there is any error and send a message. but it's better to remove panic later (it's not recommended to use it)
//***********************************************************************************************************************
func check(err error, message string) { 
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", message)
}
// Create the client structure that contains the client's name and the connection
//****************************************************************************

type ClientJob struct { 
	name string
	conn net.Conn
	
}

// Create the function,the channel with type struct
//*********************************************************

func generateResponses(clientJobs chan ClientJob) {
	for {
		// Wait for the next job to come off the queue.
		clientJob := <-clientJobs

		// Do something thats keeps the CPU buys for a whole second.
		for start := time.Now(); time.Now().Sub(start) < time.Second; {
		}

		// Send back the response.

		clientJob.conn.Write([]byte("Hello, " + clientJob.name))

		//clientJob:=make(chan ClientJob)
		//name:=make(chan string)
		//name:=<-clientJobs

		all := make(map[string]string) // create the map 
		all["client request"] =  clientJob.name// append the client requests on map
		fmt.Println("All requests in the slice", all) // show all the requests in the map

		
		
	}
		
}

// The main function
//***********************************

func main() {
	clientJobs := make(chan ClientJob) // declare the channel used in the function above
	go generateResponses(clientJobs)  // put the function in a goroutine
	
	ln, err := net.Listen("tcp", ":8080") // connect to the port 8080, it can be changed to any port if needed
	check(err, "Server is ready.") // show the message that the server is ready to receive
	//fmt.Println(<-clientJobs)
	// start checking the connection et receiving from the client and pass it into a goroutine and send it via a channel ClientJobs<-ClientJob{name, conn}
	for {
		conn, err := ln.Accept()
		check(err, "Accepted connection.")

		go func() {
			buf := bufio.NewReader(conn)

			for {
				name, err := buf.ReadString('\n')

				if err != nil {
					fmt.Printf("Client disconnected.\n")
					break
				}

				clientJobs <- ClientJob{name, conn} // pass the name and conn to the clientJobs channel
				
			}
				
	
		}()
	}
}

更多关于Golang如何在不覆盖现有值的情况下通过客户端请求填充Map的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

Go语言中的映射(map)类似于其他语言中的哈希表。映射中的键是唯一的,这意味着一个键在映射中只会出现一次,例如:

x := make(map[string]int)
x[client_request] = 1
x[client_request] = 2

无论键 client_request 在映射中出现多少次,只有最后一个值会保留在映射中。 你的代码中发生的情况是:每个客户端的请求都以键 client request 插入到映射中,并且对于所有请求,这个键是相同的。 你需要做的是为每个客户端请求使用不同的键。 只需在后面附加一个随机数或时间戳,之后你就能在映射中看到所有请求。将 func generateResponses 的最后几行替换为以下代码:

               all := make(map[string]string) // 创建映射
		key:= "client request"+ strconv.FormatInt(time.Now().Unix(),10)
		all[key] =  clientJob.name// 将客户端请求附加到映射中
		fmt.Println("All requests in the slice", all) // 显示映射中的所有请求

time.Now().Unix() 返回 int64 类型的时间戳,strconv.FormatInt(ts,10) 将时间戳转换为字符串。

要深入了解映射,请查阅这篇关于 Go语言中的映射 的文章,并按照这些 通过测试学习Go 教程进行更好的学习。

更多关于Golang如何在不覆盖现有值的情况下通过客户端请求填充Map的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在你的代码中,每次处理请求时都会创建一个新的映射,这就是为什么之前的请求会被"覆盖"的原因。实际上,你并没有覆盖之前的请求,而是每次都在创建一个全新的空映射。

要解决这个问题,你需要将映射移到函数外部,使其成为持久化的存储。以下是修改后的代码:

package main

import (
	"bufio"
	"fmt"
	"net"
	"sync"
	"time"
)

func check(err error, message string) {
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", message)
}

type ClientJob struct {
	name string
	conn net.Conn
}

// 创建全局映射和互斥锁来保证并发安全
var (
	allRequests = make(map[string]int) // 使用string作为键,int作为值(可以是请求计数)
	mu          sync.RWMutex           // 读写锁
	requestID   int                    // 用于生成唯一键
)

func generateResponses(clientJobs chan ClientJob) {
	for {
		clientJob := <-clientJobs

		for start := time.Now(); time.Now().Sub(start) < time.Second; {
		}

		clientJob.conn.Write([]byte("Hello, " + clientJob.name))

		// 使用互斥锁保护对映射的并发访问
		mu.Lock()
		requestID++
		key := fmt.Sprintf("request_%d", requestID)
		allRequests[key] = requestID
		mu.Unlock()

		// 读取时也需要加锁
		mu.RLock()
		fmt.Println("All requests in the map:", allRequests)
		mu.RUnlock()
	}
}

func main() {
	clientJobs := make(chan ClientJob)
	go generateResponses(clientJobs)

	ln, err := net.Listen("tcp", ":8080")
	check(err, "Server is ready.")

	for {
		conn, err := ln.Accept()
		check(err, "Accepted connection.")

		go func() {
			buf := bufio.NewReader(conn)

			for {
				name, err := buf.ReadString('\n')

				if err != nil {
					fmt.Printf("Client disconnected.\n")
					break
				}

				clientJobs <- ClientJob{name, conn}
			}
		}()
	}
}

如果你想要存储客户端名称而不是简单的计数,可以这样修改映射:

var (
	allRequests = make(map[int]string) // 使用int作为键,string作为值存储客户端名称
	mu          sync.RWMutex
	requestID   int
)

func generateResponses(clientJobs chan ClientJob) {
	for {
		clientJob := <-clientJobs

		for start := time.Now(); time.Now().Sub(start) < time.Second; {
		}

		clientJob.conn.Write([]byte("Hello, " + clientJob.name))

		mu.Lock()
		requestID++
		allRequests[requestID] = clientJob.name
		mu.Unlock()

		mu.RLock()
		fmt.Println("All client requests in the map:")
		for id, name := range allRequests {
			fmt.Printf("  Request %d: %s", id, name)
		}
		mu.RUnlock()
	}
}

或者如果你想要使用客户端名称作为键,并存储连接时间等信息:

type RequestInfo struct {
	Name      string
	Timestamp time.Time
	Conn      net.Conn
}

var (
	allRequests = make(map[string]RequestInfo) // 使用客户端名称作为键
	mu          sync.RWMutex
)

func generateResponses(clientJobs chan ClientJob) {
	for {
		clientJob := <-clientJobs

		for start := time.Now(); time.Now().Sub(start) < time.Second; {
		}

		clientJob.conn.Write([]byte("Hello, " + clientJob.name))

		mu.Lock()
		// 如果客户端名称可能重复,可以添加时间戳或其他唯一标识
		key := clientJob.name + "_" + time.Now().Format("20060102150405.000")
		allRequests[key] = RequestInfo{
			Name:      clientJob.name,
			Timestamp: time.Now(),
			Conn:      clientJob.conn,
		}
		mu.Unlock()

		mu.RLock()
		fmt.Printf("Total requests in map: %d\n", len(allRequests))
		for key, info := range allRequests {
			fmt.Printf("  Key: %s, Name: %s, Time: %v\n", key, info.Name, info.Timestamp)
		}
		mu.RUnlock()
	}
}

关键点是:

  1. 将映射声明为全局变量(或在适当的作用域内)
  2. 使用 sync.RWMutex 来保证并发安全
  3. 确保每个请求都有唯一的键,这样就不会覆盖之前的条目
回到顶部