Go中的双向连接

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

Two way connections in Go

问题

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

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

这是服务器的两个函数:

func runServer() {
    // 监听端口
    listen, error := net.Listen("tcp", ":8272")

    // 处理可能的错误
    if error != nil {
        fmt.Println(error)
        return
    }

    fmt.Println("正在监听端口 8272.")

    for {
        // 接受连接
        con, error := listen.Accept()

        // 处理可能的错误
        if error != nil {
            fmt.Println(error)
            continue
        }

        fmt.Println("连接已接受.")

        // 处理连接
        go handleConnection(con)
    }
}

func handleConnection(con net.Conn) {
    fmt.Println("处理连接.")

    var message string

    // 解码接收到的消息
    decoder := gob.NewDecoder(con)
    error := decoder.Decode(&message)

    // 检查错误
    if error != nil {
        fmt.Println(error)
    } else {
        fmt.Println("接收到消息:", message)
    }

    // 关闭连接
    con.Close()
    fmt.Println("连接已关闭.")
}

这是客户端的函数:

func runClient() {
    // 连接服务器
    con, error := net.Dial("tcp", "127.0.0.1:8272")

    // 处理可能的错误
    if error != nil {
        fmt.Println(error)
        return
    }

    fmt.Println("已连接到 127.0.0.1:8272.")

    // 发送消息
    message := "Hello world"
    encoder := gob.NewEncoder(con)
    error = encoder.Encode(message)

    // 检查错误
    if error != nil {
        fmt.Println(error)
    }

    con.Close()

    fmt.Println("消息已发送。连接已关闭.")
}

希望能对你有所帮助。

英文:

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:

func runServer() {
	// Listen on a port
	listen, error := net.Listen("tcp", ":8272")

	// Handles eventual errors
	if error != nil {
		fmt.Println(error)
		return
	}

	fmt.Println("Listening in port 8272.")

	for {
		// Accepts connections
		con, error := listen.Accept()

		// Handles eventual errors
		if error != nil {
			fmt.Println(error)
			continue
		}

		fmt.Println("Connection accepted.")

		// Handles the connection
		go handleConnection(con)
	}
}

func handleConnection(con net.Conn) {
	fmt.Println("Handling connection.")

	var message string

	// Decodes the received message
	decoder := gob.NewDecoder(con)
	error := decoder.Decode(&message)

	// Checks for errors
	if error != nil {
		fmt.Println(error)
	} else {
		fmt.Println("Received", message)
	}

	// Closes the connection
	con.Close()
	fmt.Println("Connection closed.")
}

This is the function of the client:

func runClient() {
	// Connects to server
	con, error := net.Dial("tcp", "127.0.0.1:8272")

	// Handles eventual errors
	if error != nil {
		fmt.Println(error)
		return
	}

	fmt.Println("Connected to 127.0.0.1:8272.")

	// Sends a message
	message := "Hello world"
	encoder := gob.NewEncoder(con)
	error = encoder.Encode(message)

	// Checks for errors
	if error != nil {
		fmt.Println(error)
	}

	con.Close()

	fmt.Println("Message sent. Connection closed.")
}

Thanks in advance.

答案1

得分: 4

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

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

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

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

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

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

func handleConnection(con net.Conn) {
    fmt.Println("处理连接。")
    
    defer func() {
        // 关闭连接
        con.Close()
        fmt.Println("连接已关闭。")
    }()
    
    var message string
    
    // 解码接收到的消息
    decoder := gob.NewDecoder(con)
    encoder := gob.NewEncoder(con)
    for {
        error := decoder.Decode(&message)
        
        // 检查错误
        if error != nil {
            fmt.Println(error)
            // 退出循环
            return
        } else {
            fmt.Println("接收到:", message)
            // 发送回复
            if error = encoder.Encode(message); error != nil {
                fmt.Println(error)
                return
            } else {
                fmt.Println("回显成功!等待下一条消息...")
            }
        }
    }
}

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

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

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

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

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:

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

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

go func(c net.Conn) {
    // Infinite loop: Get data, copy them and start over
    for {
        // Echo all incoming data.
        io.Copy(c, c)
    }
    // Shut down the connection.
    c.Close()
}(conn)

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

func handleConnection(con net.Conn) {
    fmt.Println("Handling connection.")
    
    defer func() {
        // Closes the connection
        con.Close()
        fmt.Println("Connection closed.")
    }()

    var message string
    
    // Decodes the received message
    decoder := gob.NewDecoder(con)
    encoder := gob.NewEncoder(con)
    for {
        error := decoder.Decode(&message)

        // Checks for errors
        if error != nil {
            fmt.Println(error)
            // Exit the loop
            return
        } else {
            fmt.Println("Received", message)
            // Sending back
            if error = encoder.Encode(message); error != nil {
                 fmt.Println(error)
                 return
            } else {
                fmt.Println("Echo'd successfuly ! Waiting for next message...")
            }
        }
    }
}

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:

LOOP:
    Send data (e.g. encoder.Encode)
    Receive data (e.g. decoder.Decode)
    if problem or termination -> break out of loop
END
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:

确定