Golang TCP服务器应用CPU占用过高问题已解决
Golang TCP服务器应用CPU占用过高问题已解决 我正在尝试在TCP服务器应用中使用以下代码,运行正常但CPU使用率达到了100%。有人能帮我吗?
package main
import (
"bufio"
"bytes"
"encoding/hex"
"log"
"net"
"strings"
)
// Client holds info about connection
type TCPClient struct {
conn net.Conn
Server *server
}
// TCP server
type server struct {
clients []*TCPClient
address string // Address to open connection: localhost:9999
onNewClientCallback func(c *TCPClient)
onClientConnectionClosed func(c *TCPClient, err error)
onNewMessage func(c *TCPClient, message string)
}
// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
func ScanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, []byte{'\r'}); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
return 0, nil, nil
}
// Read client data from channel
func (c *TCPClient) listen() {
for {
message, _ := bufio.NewReader(c.conn).ReadString('\r')
Rec := strings.TrimSpace(strings.ToUpper(string(message)))
c.Server.onNewMessage(c, strings.Replace(Rec, "\r", "", -1))
}
go c.listen()
}
// Send text message to client
func (c *TCPClient) Send(message string) error {
hexbytes, _ := hex.DecodeString(message)
_, err := c.conn.Write([]byte(hexbytes))
return err
}
func (c *TCPClient) Conn() net.Conn {
return c.conn
}
func (c *TCPClient) Close() error {
return c.conn.Close()
}
// Called right after server starts listening new client
func (s *server) OnNewClient(callback func(c *TCPClient)) {
s.onNewClientCallback = callback
}
// Called right after connection closed
func (s *server) OnClientConnectionClosed(callback func(c *TCPClient, err error)) {
s.onClientConnectionClosed = callback
}
// Called when Client receives new message
func (s *server) OnNewMessage(callback func(c *TCPClient, message string)) {
s.onNewMessage = callback
}
// Start network server
func (s *server) Listen() {
listener, err := net.Listen("tcp", s.address)
if err != nil {
log.Fatal("Error starting TCP server.")
}
defer listener.Close()
for {
conn, _ := listener.Accept()
client := &TCPClient{
conn: conn,
Server: s,
}
go client.listen()
s.onNewClientCallback(client)
}
}
// Creates new tcp server instance
func NewTCP(address string) *server {
server := &server{
address: address,
}
server.OnNewClient(func(c *TCPClient) {})
server.OnNewMessage(func(c *TCPClient, message string) {})
server.OnClientConnectionClosed(func(c *TCPClient, err error) {})
return server
}
更多关于Golang TCP服务器应用CPU占用过高问题已解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你是否尝试过在连接到服务器时关闭客户端命令窗口?服务器会立即崩溃…
更多关于Golang TCP服务器应用CPU占用过高问题已解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我已经解决了我的问题,特别感谢Benjamin和@johandalabacka。
谢谢。
我建议您在循环中加入一个小的延迟(例如100毫秒,特别是在listen()中的循环),以避免处理器过载。
我已经尝试发布了3次,但即使内容放在[code][/code]标签里,显示效果还是这样,我猜这应该是论坛脚本的一个bug。
func main() {
fmt.Println("hello world")
}
在循环中执行处理器密集型任务确实会导致机器过载。我认为更好的方法不是实时处理请求,而是将请求加入队列并使用 Go 协程进行后处理,或者直接将请求传递给 Go 协程处理。
这个服务器正在开发用于接收来自手机的数万条消息,每部手机每分钟发送一条消息。如果我在TCP服务器上设置延迟,在休眠期间会丢失太多消息。
问题还不止于此,目前我只有一部手机每分钟发送一个80字节的字符串。
我尝试将客户端监听改为这样
func (c *TCPClient) listen() {
scanner := bufio.NewScanner(c.conn)
for scanner.Scan() {
message := scanner.Text()
fmt.Println(message)
Rec := strings.TrimSpace(strings.ToUpper(message))
fmt.Println(Rec)
c.Server.onNewMessage(c, Rec)
}
}
它同时打印了消息和Rec,但在我的MacBook Pro(去年最便宜的型号)上,即使被一个telnet客户端访问,CPU使用率也从未超过0%。另一个想法是,如果你计划从客户端go协程访问服务器,请确保处理好这个问题,这样go就可以并行调用相同的函数。
针对你的 listen 函数,有几个可能需要考虑修改的地方(请查看注释)。程序崩溃是因为你没有捕获错误,这里的情况是客户端断开连接时产生的 EOF 错误 - 如果你愿意,可以在这里处理实际的错误情况,因为并不总是 EOF 错误,但这只是个示例:
// Read client data from channel
func (c *TCPClient) listen() {
// First: Want to close when done.
defer c.Close()
for {
message, err := bufio.NewReader(c.conn).ReadString('\r')
// Second: Need to catch this error or it will continually read.
if err != nil {
return
}
Rec := strings.TrimSpace(strings.ToUpper(string(message)))
c.Server.onNewMessage(c, strings.Replace(Rec, "\r", "", -1))
}
// This: This isn't needed.
// go c.listen()
}
另外刚刚注意到最后一个回复中使用了 scanner,这是个不错的选择,但如果你使用那个解决方案,请确保在循环后也检查 scanner 错误(顺便说一句,你仍然需要确保关闭客户端连接):
if err := scanner.Err(); err != nil { ... }
在您的TCP服务器代码中,CPU占用率达到100%的主要原因是listen()方法中的无限循环实现方式有问题。每次循环都创建新的bufio.Reader,并且没有正确处理读取错误和连接关闭。
以下是修复后的关键代码部分:
// Read client data from channel
func (c *TCPClient) listen() {
defer c.conn.Close()
reader := bufio.NewReader(c.conn)
for {
message, err := reader.ReadString('\r')
if err != nil {
if err == io.EOF {
log.Printf("Client %s disconnected", c.conn.RemoteAddr())
} else {
log.Printf("Read error: %v", err)
}
c.Server.onClientConnectionClosed(c, err)
return
}
Rec := strings.TrimSpace(strings.ToUpper(string(message)))
c.Server.onNewMessage(c, strings.Replace(Rec, "\r", "", -1))
}
}
主要修改点:
- 移除循环内的重复Reader创建:在循环外部创建
bufio.NewReader(c.conn),避免每次循环都创建新Reader - 添加错误处理:检查
ReadString的返回错误,当连接关闭或出现错误时退出循环 - 移除递归调用:删除了
go c.listen()这行,防止无限递归创建goroutine - 添加连接关闭处理:使用defer确保连接正确关闭
完整的服务器启动示例:
func main() {
server := NewTCP("localhost:9999")
server.OnNewClient(func(c *TCPClient) {
log.Printf("New client connected: %s", c.Conn().RemoteAddr())
})
server.OnNewMessage(func(c *TCPClient, message string) {
log.Printf("Received message from %s: %s", c.Conn().RemoteAddr(), message)
})
server.OnClientConnectionClosed(func(c *TCPClient, err error) {
log.Printf("Client disconnected: %s, error: %v", c.Conn().RemoteAddr(), err)
})
server.Listen()
}
这些修改将显著降低CPU使用率,因为现在每个连接只有一个Reader实例,并且当连接关闭时会正确退出循环,避免了无限循环和资源泄漏。


