英文:
Socket echo server in go
问题
我正在尝试在Go中实现一个简单的socket回显服务器,以下是代码:
package main
import (
"fmt"
"net"
"sync"
)
func echo_srv(c net.Conn, wg sync.WaitGroup) {
defer c.Close()
defer wg.Done()
for {
var msg []byte
n, err := c.Read(msg)
if err != nil {
fmt.Printf("ERROR: read\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: received %v bytes\n", n)
n, err = c.Write(msg)
if err != nil {
fmt.Printf("ERROR: write\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: sent %v bytes\n", n)
}
}
func main() {
var wg sync.WaitGroup
ln, err := net.Listen("unix", "./sock_srv")
if err != nil {
fmt.Print(err)
return
}
defer ln.Close()
conn, err := ln.Accept()
if err != nil {
fmt.Print(err)
return
}
wg.Add(1)
go echo_srv(conn, wg)
wg.Wait()
}
由于某种原因,一旦客户端连接,c.Read()就不会阻塞并打印错误消息。
所以,我的第一个问题是:c.Read()不应该阻塞直到客户端向套接字发送数据吗?
第二个问题是:在打印错误消息后,服务器不会终止。
在gdb中执行程序时,我看到的是:
(gdb) run
Starting program: src/sockets/server/server
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffe7806700 (LWP 28594)]
[New Thread 0x7fffe7005700 (LWP 28595)]
ERROR: read
EOF^C
Program received signal SIGINT, Interrupt.
runtime.epollwait () at /usr/lib/go/src/pkg/runtime/sys_linux_amd64.s:383
383 RET
(gdb) info goroutines
1 waiting runtime.park
2 syscall runtime.goexit
* 3 syscall runtime.entersyscallblock
我在Python和C中也有类似的回显服务器,它们工作正常。为了完整起见,我还发布了下面的socket客户端应用程序(它与我的C和Python服务器正常工作)。
客户端:
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
conn, err := net.Dial("unix", "./sock_srv")
if err != nil {
fmt.Print(err)
return
}
defer conn.Close()
for {
fmt.Print("Enter message to transmit: ")
msg, err := stdin.ReadString('\n')
if err != nil {
fmt.Print(err)
return
}
msg = msg[:len(msg)-1]
if (strings.ToLower(msg) == "quit") || (strings.ToLower(msg) == "exit") {
fmt.Println("bye")
return
}
n, err := conn.Write([]byte(msg))
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("CLIENT: sent %v bytes\n", n)
n, err = conn.Read([]byte(msg))
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("CLIENT: received %v bytes\n", n)
fmt.Println("Received message:", msg)
}
}
英文:
I'm trying to implement a simple socket echo server in go this is the code:
package main
import (
"fmt"
"net"
"sync"
)
func echo_srv(c net.Conn, wg sync.WaitGroup) {
defer c.Close()
defer wg.Done()
for {
var msg []byte
n, err := c.Read(msg)
if err != nil {
fmt.Printf("ERROR: read\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: received %v bytes\n", n)
n, err = c.Write(msg)
if err != nil {
fmt.Printf("ERROR: write\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: sent %v bytes\n", n)
}
}
func main() {
var wg sync.WaitGroup
ln, err := net.Listen("unix", "./sock_srv")
if err != nil {
fmt.Print(err)
return
}
defer ln.Close()
conn, err := ln.Accept()
if err != nil {
fmt.Print(err)
return
}
wg.Add(1)
go echo_srv(conn, wg)
wg.Wait()
}
For some reason as soon as a client connects, c.Read() does not block and the error message is printed.
So, my first question is: Shouldn't c.Read() block until a client sends something to the socket?
And second: After printing the error message, the server does not terminate.
This is what I see when executing the program in gdb:
(gdb) run
Starting program: src/sockets/server/server
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffe7806700 (LWP 28594)]
[New Thread 0x7fffe7005700 (LWP 28595)]
ERROR: read
EOF^C
Program received signal SIGINT, Interrupt.
runtime.epollwait () at /usr/lib/go/src/pkg/runtime/sys_linux_amd64.s:383
383 RET
(gdb) info goroutines
1 waiting runtime.park
2 syscall runtime.goexit
* 3 syscall runtime.entersyscallblock
I have similar echo servers in Python and C and they work fine. For completeness I also post the socket client application below (it works fine with my C and Python servers).
Client:
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
conn, err := net.Dial("unix", "./sock_srv")
if err != nil {
fmt.Print(err)
return
}
defer conn.Close()
for {
fmt.Print("Enter message to transmit: ")
msg, err := stdin.ReadString('\n')
if err != nil {
fmt.Print(err)
return
}
msg = msg[:len(msg)-1]
if (strings.ToLower(msg) == "quit") || (strings.ToLower(msg) == "exit") {
fmt.Println("bye")
return
}
n, err := conn.Write([]byte(msg))
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("CLIENT: sent %v bytes\n", n)
n, err = conn.Read([]byte(msg))
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("CLIENT: received %v bytes\n", n)
fmt.Println("Received message:", msg)
}
}
答案1
得分: 5
这是一个可工作的echo_srv
给你。你还需要@jnml的建议!
- 实际上分配一些缓冲区来接收 - 你创建了一个0字节的缓冲区!
- 在EOF时优雅地退出
- 只写入使用
msg[:n]
接收到的字节
func echo_srv(c net.Conn, wg *sync.WaitGroup) {
defer c.Close()
defer wg.Done()
for {
msg := make([]byte, 1000)
n, err := c.Read(msg)
if err == io.EOF {
fmt.Printf("SERVER: received EOF (%d bytes ignored)\n", n)
return
} else if err != nil {
fmt.Printf("ERROR: read\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: received %v bytes\n", n)
n, err = c.Write(msg[:n])
if err != nil {
fmt.Printf("ERROR: write\n")
fmt.Print(err)
return
}
fmt.Printf("SERVER: sent %v bytes\n", n)
}
}
英文:
Here is a working echo_srv
for you. You'll need @jnml's suggestion too!
-
actually allocate some buffer to receive into - you made a 0 byte buffer!
-
exit neatly on EOF
-
only write the bytes received with
msg[:n]
func echo_srv(c net.Conn, wg *sync.WaitGroup) { defer c.Close() defer wg.Done() for { msg := make([]byte, 1000) n, err := c.Read(msg) if err == io.EOF { fmt.Printf("SERVER: received EOF (%d bytes ignored)\n", n) return } else if err != nil { fmt.Printf("ERROR: read\n") fmt.Print(err) return } fmt.Printf("SERVER: received %v bytes\n", n) n, err = c.Write(msg[:n]) if err != nil { fmt.Printf("ERROR: write\n") fmt.Print(err) return } fmt.Printf("SERVER: sent %v bytes\n", n) } }
答案2
得分: 3
我没有检查它是否是罪魁祸首,但在“技术分析”方面,我注意到你的代码中有一个错误:你将sync.Workgroup
的副本传递给了echo_srv
。对副本所做的任何更改都不会对原始实例产生影响。
将echo
的签名更改为:
func echo_srv(c net.Conn, wg *sync.WaitGroup)
然后像这样调用它:
go echo_srv(conn, &wg)
顺便说一下:在惯用的Go代码命名中,不使用下划线(_
)作为名称的一部分。惯用的名称应该是例如echoSrv
。
英文:
I did not check if it's the culprit, but on the "technical analysis" side I noticed one error in your code: You're passing a copy of a sync.Workgroup
to echo_srv
. Any changes made to the copy are not effective to the original instance.
Change the signature of echo
to:
func echo_srv(c net.Conn, wg *sync.WaitGroup)
and then call it like:
go echo_srv(conn, &wg)
On a side note: Underscores (_
) are not used in the middle of idiomatic Go code names. The idiomatic name would be eg. echoSrv
instead.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论