Go中的双向连接

huangapple go评论112阅读模式
英文:

Two way connections in Go

问题

我正在尝试使用Go语言制作一个简单的控制台聊天程序,只是为了练习。然而,我不知道如何从服务器发送消息回来。服务器只接收消息然后关闭连接。我该如何发送响应呢?

我已经搜索到了关于WebSockets的信息,但我认为它们是用于与浏览器进行交互的。

这是服务器的两个函数:

  1. func runServer() {
  2. // 监听端口
  3. listen, error := net.Listen("tcp", ":8272")
  4. // 处理可能的错误
  5. if error != nil {
  6. fmt.Println(error)
  7. return
  8. }
  9. fmt.Println("正在监听端口 8272.")
  10. for {
  11. // 接受连接
  12. con, error := listen.Accept()
  13. // 处理可能的错误
  14. if error != nil {
  15. fmt.Println(error)
  16. continue
  17. }
  18. fmt.Println("连接已接受.")
  19. // 处理连接
  20. go handleConnection(con)
  21. }
  22. }
  23. func handleConnection(con net.Conn) {
  24. fmt.Println("处理连接.")
  25. var message string
  26. // 解码接收到的消息
  27. decoder := gob.NewDecoder(con)
  28. error := decoder.Decode(&message)
  29. // 检查错误
  30. if error != nil {
  31. fmt.Println(error)
  32. } else {
  33. fmt.Println("接收到消息:", message)
  34. }
  35. // 关闭连接
  36. con.Close()
  37. fmt.Println("连接已关闭.")
  38. }

这是客户端的函数:

  1. func runClient() {
  2. // 连接服务器
  3. con, error := net.Dial("tcp", "127.0.0.1:8272")
  4. // 处理可能的错误
  5. if error != nil {
  6. fmt.Println(error)
  7. return
  8. }
  9. fmt.Println("已连接到 127.0.0.1:8272.")
  10. // 发送消息
  11. message := "Hello world"
  12. encoder := gob.NewEncoder(con)
  13. error = encoder.Encode(message)
  14. // 检查错误
  15. if error != nil {
  16. fmt.Println(error)
  17. }
  18. con.Close()
  19. fmt.Println("消息已发送。连接已关闭.")
  20. }

希望能对你有所帮助。

英文:

I am trying to make a simple console chat in Go, just to practice. However, I don't know how to send messages back from the server. The server just receives a message and then closes the connection. How could I send a response?

I have been searching and found information about websockets, but I think that they are used to interact with browsers.

This are the two functions of the server:

  1. func runServer() {
  2. // Listen on a port
  3. listen, error := net.Listen("tcp", ":8272")
  4. // Handles eventual errors
  5. if error != nil {
  6. fmt.Println(error)
  7. return
  8. }
  9. fmt.Println("Listening in port 8272.")
  10. for {
  11. // Accepts connections
  12. con, error := listen.Accept()
  13. // Handles eventual errors
  14. if error != nil {
  15. fmt.Println(error)
  16. continue
  17. }
  18. fmt.Println("Connection accepted.")
  19. // Handles the connection
  20. go handleConnection(con)
  21. }
  22. }
  23. func handleConnection(con net.Conn) {
  24. fmt.Println("Handling connection.")
  25. var message string
  26. // Decodes the received message
  27. decoder := gob.NewDecoder(con)
  28. error := decoder.Decode(&message)
  29. // Checks for errors
  30. if error != nil {
  31. fmt.Println(error)
  32. } else {
  33. fmt.Println("Received", message)
  34. }
  35. // Closes the connection
  36. con.Close()
  37. fmt.Println("Connection closed.")
  38. }

This is the function of the client:

  1. func runClient() {
  2. // Connects to server
  3. con, error := net.Dial("tcp", "127.0.0.1:8272")
  4. // Handles eventual errors
  5. if error != nil {
  6. fmt.Println(error)
  7. return
  8. }
  9. fmt.Println("Connected to 127.0.0.1:8272.")
  10. // Sends a message
  11. message := "Hello world"
  12. encoder := gob.NewEncoder(con)
  13. error = encoder.Encode(message)
  14. // Checks for errors
  15. if error != nil {
  16. fmt.Println(error)
  17. }
  18. con.Close()
  19. fmt.Println("Message sent. Connection closed.")
  20. }

Thanks in advance.

答案1

得分: 4

您的con对象是一个连接,其中包含了ReadWrite方法,可以在这里找到详细描述:这里。您应该在连接上循环,尝试Read传入的数据,然后处理它并(可能)Write回服务器的响应。(在这里,bufferio包等可以帮助您以更方便的方式处理数据,这只是底层的ReadWriter接口)

文档中展示了一个简单的示例

  1. go func(c net.Conn) {
  2. // 回显所有传入的数据。
  3. io.Copy(c, c)
  4. // 关闭连接。
  5. c.Close()
  6. }(conn)

该示例只处理第一条消息,然后关闭连接。您可以像这样处理每条传入的消息:

  1. go func(c net.Conn) {
  2. // 无限循环:获取数据,复制它们,然后重新开始
  3. for {
  4. // 回显所有传入的数据。
  5. io.Copy(c, c)
  6. }
  7. // 关闭连接。
  8. c.Close()
  9. }(conn)

当然,您可以根据您的服务器将io.Copy替换为相应的处理逻辑,所以您的示例可能是这样的:

  1. func handleConnection(con net.Conn) {
  2. fmt.Println("处理连接。")
  3. defer func() {
  4. // 关闭连接
  5. con.Close()
  6. fmt.Println("连接已关闭。")
  7. }()
  8. var message string
  9. // 解码接收到的消息
  10. decoder := gob.NewDecoder(con)
  11. encoder := gob.NewEncoder(con)
  12. for {
  13. error := decoder.Decode(&message)
  14. // 检查错误
  15. if error != nil {
  16. fmt.Println(error)
  17. // 退出循环
  18. return
  19. } else {
  20. fmt.Println("接收到:", message)
  21. // 发送回复
  22. if error = encoder.Encode(message); error != nil {
  23. fmt.Println(error)
  24. return
  25. } else {
  26. fmt.Println("回显成功!等待下一条消息...")
  27. }
  28. }
  29. }
  30. }

此外,您可能应该在日志消息中使用log包而不是fmt包(但这在这里并不重要)。

了解它是如何工作的一个好方法是浏览默认的http服务器的实现代码这里

类似地,您的客户端应该使用相同的模式进行循环:

  1. LOOP:
  2. 发送数据(例如 encoder.Encode
  3. 接收数据(例如 decoder.Decode
  4. 如果有问题或终止 -> 退出循环
  5. END
  6. 关闭连接
英文:

Your con object is a connection, which has Read and Write methods described here: here. You should loop on the connection, trying to Read incoming data, then process it and (possibly) Write back the server response. (Here, bufferiopackage and the like can help you process it in a more convenient way, this is just the low-level ReadWriter interface)

The documentation shows a tiny example:

  1. go func(c net.Conn) {
  2. // Echo all incoming data.
  3. io.Copy(c, c)
  4. // Shut down the connection.
  5. c.Close()
  6. }(conn)

Which processes only the first message and then closes. You can process each incoming message like this:

  1. go func(c net.Conn) {
  2. // Infinite loop: Get data, copy them and start over
  3. for {
  4. // Echo all incoming data.
  5. io.Copy(c, c)
  6. }
  7. // Shut down the connection.
  8. c.Close()
  9. }(conn)

Replacing io.Copy with whatever is relevant to your server of course, so your example would be something like this:

  1. func handleConnection(con net.Conn) {
  2. fmt.Println("Handling connection.")
  3. defer func() {
  4. // Closes the connection
  5. con.Close()
  6. fmt.Println("Connection closed.")
  7. }()
  8. var message string
  9. // Decodes the received message
  10. decoder := gob.NewDecoder(con)
  11. encoder := gob.NewEncoder(con)
  12. for {
  13. error := decoder.Decode(&message)
  14. // Checks for errors
  15. if error != nil {
  16. fmt.Println(error)
  17. // Exit the loop
  18. return
  19. } else {
  20. fmt.Println("Received", message)
  21. // Sending back
  22. if error = encoder.Encode(message); error != nil {
  23. fmt.Println(error)
  24. return
  25. } else {
  26. fmt.Println("Echo'd successfuly ! Waiting for next message...")
  27. }
  28. }
  29. }
  30. }

Also, you should probably use package log instead of fmt for your logging messages (but this is irrelevant here).

A good place to understand how it all works is to browse the implementation of the default http server here.

Similarly, your client should loop using the same pattern:

  1. LOOP:
  2. Send data (e.g. encoder.Encode)
  3. Receive data (e.g. decoder.Decode)
  4. if problem or termination -> break out of loop
  5. END
  6. close connection

huangapple
  • 本文由 发表于 2013年8月14日 19:57:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/18231129.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定