Сокет Голанга не отправляет все байты до того, как метод Write () вернется - PullRequest
0 голосов
/ 11 ноября 2018

Сегодня, когда я пытаюсь отправить данные 100M на мой сервер (очень простой TCP-сервер, также написанный на Golang), я обнаружил, что метод TCPConn.Write возвращает 104857600 и ошибку nil, а затем закрываю сокет. Но мой сервер только получает очень мало данных. Я думаю, это потому, что метод Write работает в асинхронном режиме, поэтому, хотя метод возвращает 104857600, на сервер отправляется только небольшое количество данных. Поэтому я хочу знать, есть ли способ установить работу записи в режиме синхронизации или как определить, все ли данные отправляются на сервер из сокета.

Код выглядит следующим образом: Сервер:

const ListenAddress = "192.168.0.128:8888"
func main() {

    var l net.Listener
    var err error
    l, err = net.Listen("tcp", ListenAddress)
    if err != nil {
        fmt.Println("Error listening:", err)
        os.Exit(1)
    }
    defer l.Close()
    fmt.Println("listen on " + ListenAddress)
    for {
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err)
            os.Exit(1)
        }
        //logs an incoming message
        fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr())
        // Handle connections in a new goroutine.
        go handleRequest(conn)
    }
}
func handleRequest(conn net.Conn) {
    defer conn.Close()
    rcvLen := 0
    rcvData := make([]byte,20 * 1024 * 1024) // 20M
    for {
        l , err := conn.Read(rcvData)
        if err != nil {
            fmt.Printf("%v", err)
            return
        }
        rcvLen += l
        fmt.Printf("recv: %d\r\n", rcvLen)

        conn.Write(rcvData[:l])
    }
}

Клиент:

conn, err := net.Dial("tcp", "192.168.0.128:8888")
if err != nil {
    fmt.Println(err)
    os.Exit(-1)
}
defer conn.Close()
data := make([]byte, 500 * 1024 * 1024)
len, err := conn.Write(data)
fmt.Println("send len: ", len)

Выход клиента:

send len:  524288000

Выход сервера:

listen on 192.168.0.128:8888
Received message 192.168.0.2:50561 -> 192.168.0.128:8888 
recv: 166440
recv: 265720
EOF

Я знаю, что если я смогу заставить клиента некоторое время подождать методом SetLinger, все данные будут отправлены на сервер до закрытия сокета. Но я хочу найти способ заставить сокет отправлять все данные перед возвратом без вызова SetLinger (). Заранее спасибо. Пожалуйста, извините за мой плохой английский.

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

Вы не обрабатываете ошибку, возвращенную conn.Read правильно. От документы (выделено мое):

Если при чтении обнаруживается ошибка или условие конца файла после успешного чтения n> 0 байтов, возвращается число прочитанных байтов. Он может вернуть (не ноль) ошибку из того же вызова или вернуть ошибку (и n == 0) из последующего вызова. [...]

Вызывающие всегда должны обработать n> 0 байт, возвращенных до рассмотрения ошибки err . Это правильно обрабатывает ошибки ввода-вывода, которые возникают после чтения некоторых байтов, а также оба допустимых поведения EOF.

Обратите внимание, что вы заново изобретаете io.Copy (хотя и с чрезмерным размером буфера). Код вашего сервера можно переписать как:

func handleRequest(conn net.Conn) {
    defer conn.Close()
    n, err := io.Copy(conn, conn)
}
0 голосов
/ 11 ноября 2018

Опрашивали ли вы сокет перед попыткой записи?

За сокетом находится стек tcp вашей операционной системы. При записи в сокет вы помещаете байты в буфер отправки. Ваша операционная система сама определяет, когда и как отправлять. Если у принимающей стороны нет буфера space.available в их буфере приема, ваша отправляющая сторона знает об этом и не будет помещать больше информации в буфер отправки.

Убедитесь, что в вашем буфере отправки достаточно места для того, что вы пытаетесь отправить дальше. Это делается путем опроса сокета. Этот метод обычно называется Socket.Poll. Я рекомендую вам проверить документы Голанга для точного использования.

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