go - 如何使用 context.Context 与 tcp 连接读取
问题描述
我正在尝试在用户和 tcp 之间创建一个中间层,其中包含Send
和Receive
功能。目前,我正在尝试整合上下文,以便Send
andReceive
尊重上下文。但是,我不知道如何让他们尊重上下文的取消。到目前为止,我得到了以下内容。
// c.underlying is a net.Conn
func (c *tcpConn) Receive(ctx context.Context) ([]byte, error) {
if deadline, ok := ctx.Deadline(); ok {
// Set the read deadline on the underlying connection according to the
// given context. This read deadline applies to the whole function, so
// we only set it once here. On the next read-call, it will be set
// again, or will be reset in the else block, to not keep an old
// deadline.
c.underlying.SetReadDeadline(deadline)
} else {
c.underlying.SetReadDeadline(time.Time{}) // remove the read deadline
}
// perform reads with
// c.underlying.Read(myBuffer)
return frameData, nil
}
但是,据我了解该代码,这仅尊重 a context.WithTimeout
or context.WithDeadline
,而不是 a context.WithCancel
。如果可能的话,我想以某种方式将其传递给连接,或者实际上中止读取过程。
我怎样才能做到这一点?
注意:如果可能的话,我想避免另一个函数读取另一个 goroutine 并将结果推送回通道,因为那样,当调用 时cancel
,我正在通过网络读取 2GB,这实际上并没有取消读取,并且资源仍在使用中。但是,如果不能以另一种方式实现,我想知道是否有比具有两个通道的函数更好的方法,一个用于[]byte
结果,一个用于error
.
编辑: 使用以下代码,我可以尊重取消,但它不会中止读取。
// apply deadline ...
result := make(chan interface{})
defer close(result)
go c.receiveAsync(result)
select {
case res := <-result:
if err, ok := res.(error); ok {
return nil, err
}
return res.([]byte), nil
case <-ctx.Done():
return nil, ErrTimeout
}
}
func (c *tcpConn) receiveAsync(result chan interface{}) {
// perform the reads and push either an error or the
// read bytes to the result channel
解决方案
如果连接可以在取消时关闭,您可以设置一个 goroutine 以在Receive
方法内取消时关闭连接。如果稍后必须再次重用连接,则无法取消Read
正在进行的连接。
recvDone := make(chan struct{})
defer close(recvDone)
// setup the cancellation to abort reads in process
go func() {
select {
case <-ctx.Done():
c.underlying.CloseRead()
// Close() can be used if this isn't necessarily a TCP connection
case <-recvDone:
}
}()
如果您想返回取消错误,这将需要更多的工作,但是这CloseRead
将提供一种干净的方法来停止任何挂起的 TCPRead
调用。
推荐阅读
- python - 为什么我在 Google Code Jam 上获得此代码的 RE?
- c - 将转置矩阵与第二个矩阵相乘
- python - 计算库存头寸余额
- python - Azure 下游设备到带有 python 的 Edge 网关
- nexus - 将 CentOS 软件包上传到 Nexus 3 的正确方法是什么?
- flask - 全栈 React、Redux、Python/Flask、PostgreSQL Heroku 应用程序
- javascript - 赛普拉斯 - 从函数设置全局变量
- git - 如何对一个项目提出多个拉取请求?
- facebook - 如何使用 React Native 在 Facebook 上分享/发布内容?
- html - 如何对齐html表格行