Как использовать context.Context с tcp-соединением читать - PullRequest
0 голосов
/ 06 мая 2020

Я пытаюсь создать промежуточный уровень между пользователем и tcp с функциями Send и Receive. В настоящее время я пытаюсь интегрировать контекст, чтобы Send и Receive уважали контекст. Однако я не знаю, как заставить их уважать отмену контекста. До сих пор я получал следующее.

// 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
}

Однако, насколько я понимаю этот код, это учитывает только context.WithTimeout или context.WithDeadline, а не context.WithCancel. Если возможно, я хотел бы каким-то образом передать это в соединение или фактически прервать процесс чтения.

Как я могу это сделать?

Примечание: если возможно, я бы хотел избежать другого функция, которая читает в другой горутине и отправляет результат обратно в канал, потому что тогда, когда я вызываю cancel, и я читаю 2 ГБ по сети, это фактически не отменяет чтение, и ресурсы все еще используются. Однако, если это невозможно по-другому, я хотел бы знать, есть ли лучший способ сделать это, чем функция с двумя каналами, один для результата []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

1 Ответ

0 голосов
/ 06 мая 2020

Если соединение может быть закрыто при отмене, вы можете настроить горутину для отключения соединения при отмене с помощью метода 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 предоставит чистый способ остановить любые ожидающие TCP Read вызовы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...