在Go语言中,使用Socket时是否需要写缓冲区?

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

Do I need a write buffer for socket in go?

问题

假设我在Linux上有一个TCP服务器,它会为每个新连接创建一个新的goroutine。当我想要向TCP连接写入数据时,我应该像这样做:

conn.Write(data)

还是应该在专门用于写入的goroutine中进行,像这样:

func writeRoutine(sendChan chan []byte) {
    for {
        select {
        case msg := <-sendChan:
            conn.Write(msg)
        }
    }
}

以防网络繁忙。

简而言之,当在Go中写入套接字时,我是否需要像在C/C++中一样使用写缓冲区?

PS:也许我没有清楚地表达问题。

  1. 我提到了服务器,意思是在Linux上运行的TCP服务器。它会为每个新连接创建一个新的goroutine,就像这样:
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
    log.Error(err.Error())
    os.Exit(-1)
}
for {
    conn, err := listener.AcceptTCP()
    if err != nil {
        continue
    }
    log.Debug("Accept a new connection", conn.RemoteAddr())
    go handleClient(conn)
}
  1. 我认为我的问题与代码关系不大。正如我们所知,在C/C++中使用size_t write(int fd, const void *buf, size_t count);来写入套接字文件描述符时,对于TCP服务器,在代码中我们需要一个套接字的写缓冲区,否则可能只有部分数据成功写入。我的意思是,我在Go中是否也需要这样做?
英文:

Suppose I had a Tcp server in linux, it would create a new goroutine for a new connnection. When I want to write data to the tcp connection, should I do it just like this

conn.Write(data)

or do it in a goroutine especially for writing, like this

func writeRoutine(sendChan chan []byte){
      for {
       select {
       case  msg := &lt;- sendChan :
          conn.Write(msg)
       }
      }
}

just in case that the network was busy.

In a short, Did I need a write buffer in go just like in c/c++ when writing to a socket?

PS maybe I didn't exclaim the problem clearly.

1 I talked of the server, meaning a tcp server runing in linux. It would create a new goroutine for a new connnection. like this

   listener, err := net.ListenTCP(&quot;tcp&quot;, tcpAddr)
    if err != nil {
        log.Error(err.Error())
        os.Exit(-1)
    }
    for {
        conn, err := listener.AcceptTCP()
        if err != nil {
            continue
        }
        log.Debug(&quot;Accept a new connection &quot;, conn.RemoteAddr())
        go handleClient(conn)
    }

2 I think my problem isn't much concerned with the code. As we know, when we use size_t write(int fd, const void *buf, size_t count); to write a socket fd in c/c++, for a tcp server, we need a write buffer for a socket in your code necessaryly, or maybe only some of the data is writen successfully. I mean, Do I have to do so in go ?

答案1

得分: 3

你实际上在这里问了两个不同的问题:

1)在我的TCP服务器中,是否应该为每个接受的客户端连接使用一个goroutine?
2)给定一个[]byte,我应该如何写入连接?

对于第一个问题,答案是肯定的。这是Go语言最适合的模式。如果你查看 net/http 的源代码,你会发现它为每个连接生成一个goroutine。

至于第二个问题,你应该像在C/C++服务器中一样操作:写入数据,检查写入了多少,并继续写入直到完成,始终检查错误。以下是一个代码片段,展示了如何实现:

func writeConn(data []byte) error {
    var start, c int
    var err error
    for {
      if c, err = conn.Write(data[start:]); err != nil {
          return err
      }
      start += c
      if c == 0 || start == len(data) {
          break
      }
    }
    return nil
}
英文:

You are actually asking two different questions here:

  1. Should you use a goroutine per accepted client connection in my TCP server?
  2. Given a []byte, how should I write to the connection?

For 1), the answer is yes. This is the type of pattern that go is most suited for. If you take a look at the source code for the net/http, you will see that it spawns a goroutine for each connection.

As for 2), you should do the same that you would do in a c/c++ server: write, check how much was written and keep on writing until your done, always checking for errors. Here is a code snippet on how to do it:

func writeConn(data []byte) error {
    var start,c int
    var err error
    for {
      if c, err = conn.Write(data[start:]); err != nil {
          return err
      }
      start += c
      if c == 0 || start == len(data) {
          break
      }
    }
    return nil
}

答案2

得分: 1

> 服务器[...] 为新连接创建一个新的goroutine

这是有道理的,因为处理程序的goroutine可能会阻塞,而不会延迟服务器的接受循环。

如果您按顺序处理每个请求,任何阻塞的系统调用实际上都会将服务器锁定给所有客户端。

> 特别用于写入的goroutine

这只在以下情况下有意义:您要写入一个非常大的数据块或者写入一个非常慢的连接,并且您需要使处理程序继续无阻塞地运行。

请注意,这不是通常理解的“写缓冲区”。

英文:

> server [...] create a new goroutine for a new connnection

This makes sense because the handler goroutines can block without delaying the server's accept loop.

If you handled each request serially, any blocking syscall would essentially lock up the server for all clients.

> goroutine especially for writing

This would only make sense in use cases where you're writing either a really big chunk of data or to a very slow connection and you need your handler to continue unblocked, for instance.

Note that this is not what is commonly understood as a "write buffer".

huangapple
  • 本文由 发表于 2015年1月28日 16:17:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/28187519.html
匿名

发表评论

匿名网友

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

确定