2013-02-25 13 views
5

Tôi đang cố gắng phát hiện lỗi gửi bằng cách kiểm tra lỗi được trả về bởi golang TCPConn.Write, nhưng không phải. Tôi cũng đã thử sử dụng TCPConn.SetWriteDeadline mà không thành công.golang TCPConn.SetWriteDeadline dường như không hoạt động như mong đợi

Đó là cách điều xảy ra:

  1. máy chủ bắt đầu
  2. một client kết nối
  3. máy chủ gửi một thông điệp và khách hàng nhận nó
  4. client tắt
  5. sự máy chủ gửi thêm một tin nhắn: không có lỗi
  6. máy chủ gửi bản tin thứ ba ge: chỉ bây giờ lỗi xuất hiện

Câu hỏi: tại sao chỉ thông báo thứ hai cho một khách hàng không tồn tại dẫn đến lỗi? Trường hợp này nên được xử lý đúng cách như thế nào?

mã sau:

package main 

import (
    "net" 
    "os" 
    "bufio" 
    "fmt" 
    "time" 
) 

func AcceptConnections(listener net.Listener, console <- chan string) { 

    msg := "" 

    for { 

     conn, err := listener.Accept() 

     if err != nil { 
      panic(err) 
     } 

     fmt.Printf("client connected\n") 

     for { 

      if msg == "" { 
       msg = <- console 
       fmt.Printf("read from console: %s", msg) 
      } 

      err = conn.SetWriteDeadline(time.Now().Add(time.Second)) 

      if err != nil { 
       fmt.Printf("SetWriteDeadline failed: %v\n", err) 
      } 

      _, err = conn.Write([]byte(msg)) 

      if err != nil { 
       // expecting an error after sending a message 
       // to a non-existing client endpoint 
       fmt.Printf("failed sending a message to network: %v\n", err) 
       break 
      } else { 
       fmt.Printf("msg sent: %s", msg) 
       msg = "" 
      } 
     } 
    } 
} 

func ReadConsole(network chan <- string) { 

    console := bufio.NewReader(os.Stdin) 

    for { 

     line, err := console.ReadString('\n') 

     if err != nil { 

      panic(err) 

     } else { 

      network <- line 
     } 
    } 
} 

func main() { 

    listener, err := net.Listen("tcp", "localhost:6666") 

    if err != nil { 
     panic(err) 
    } 

    println("listening on " + listener.Addr().String()) 

    consoleToNetwork := make(chan string) 

    go AcceptConnections(listener, consoleToNetwork) 

    ReadConsole(consoleToNetwork) 
} 

Các máy chủ giao diện điều khiển trông như thế này:

listening on 127.0.0.1:6666 
client connected 
hi there! 
read from console: hi there! 
msg sent: hi there! 
this one should fail 
read from console: this one should fail 
msg sent: this one should fail 
this one actually fails 
read from console: this one actually fails 
failed sending a message to network: write tcp 127.0.0.1:51194: broken pipe 

Client trông như thế này:

package main 

import (
    "net" 
    "os" 
    "io" 
    //"bufio" 
    //"fmt" 
) 

func cp(dst io.Writer, src io.Reader, errc chan<- error) { 

    // -reads from src and writes to dst 
    // -blocks until EOF 
    // -EOF is not an error 
    _, err := io.Copy(dst, src) 

    // push err to the channel when io.Copy returns 
    errc <- err 
} 

func StartCommunication(conn net.Conn) { 

    //create a channel for errors 
    errc := make(chan error) 

    //read connection and print to console 
    go cp(os.Stdout, conn, errc) 

    //read user input and write to connection 
    go cp(conn, os.Stdin, errc) 

    //wait until nil or an error arrives 
    err := <- errc 

    if err != nil { 
     println("cp error: ", err.Error()) 
    } 
} 

func main() { 

    servAddr := "localhost:6666" 

    tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr) 

    if err != nil { 
     println("ResolveTCPAddr failed:", err.Error()) 
     os.Exit(1) 
    } 

    conn, err := net.DialTCP("tcp", nil, tcpAddr) 

    if err != nil { 
     println("net.DialTCP failed:", err.Error()) 
     os.Exit(1) 
    } 

    defer conn.Close() 

    StartCommunication(conn) 

} 

EDIT: Tiếp theo đề nghị JimB của tôi đã đưa ra một ví dụ làm việc. Tin nhắn không bị mất nữa và được gửi lại trong một kết nối mới. Tôi không khá chắc chắn mặc dù làm thế nào an toàn là nó để sử dụng một biến chia sẻ (connWrap.IsFaulted) giữa thói quen đi khác nhau.

package main 

import (
    "net" 
    "os" 
    "bufio" 
    "fmt" 
) 

type Connection struct { 
    IsFaulted bool 
    Conn net.Conn 
} 

func StartWritingToNetwork(connWrap * Connection, errChannel chan <- error, msgStack chan string) { 

    for { 

     msg := <- msgStack 

     if connWrap.IsFaulted { 

      //put it back for another connection 
      msgStack <- msg 

      return 
     } 

     _, err := connWrap.Conn.Write([]byte(msg)) 

     if err != nil { 

      fmt.Printf("failed sending a message to network: %v\n", err) 

      connWrap.IsFaulted = true 

      msgStack <- msg 

      errChannel <- err 

      return 

     } else { 

      fmt.Printf("msg sent: %s", msg) 
     } 
    } 
} 

func StartReadingFromNetwork(connWrap * Connection, errChannel chan <- error){ 

    network := bufio.NewReader(connWrap.Conn) 

    for (!connWrap.IsFaulted) { 

     line, err := network.ReadString('\n') 

     if err != nil { 

      fmt.Printf("failed reading from network: %v\n", err) 

      connWrap.IsFaulted = true 

      errChannel <- err 

     } else { 

      fmt.Printf("%s", line) 
     } 
    } 
} 

func AcceptConnections(listener net.Listener, console chan string) { 

    errChannel := make(chan error) 

    for { 

     conn, err := listener.Accept() 

     if err != nil { 
      panic(err) 
     } 

     fmt.Printf("client connected\n") 

     connWrap := Connection{false, conn} 

     go StartReadingFromNetwork(&connWrap, errChannel) 

     go StartWritingToNetwork(&connWrap, errChannel, console) 

     //block until an error occurs 
     <- errChannel 
    } 
} 

func ReadConsole(network chan <- string) { 

    console := bufio.NewReader(os.Stdin) 

    for { 

     line, err := console.ReadString('\n') 

     if err != nil { 

      panic(err) 

     } else { 

      network <- line 
     } 
    } 
} 

func main() { 

    listener, err := net.Listen("tcp", "localhost:6666") 

    if err != nil { 
     panic(err) 
    } 

    println("listening on " + listener.Addr().String()) 

    consoleToNetwork := make(chan string) 

    go AcceptConnections(listener, consoleToNetwork) 

    ReadConsole(consoleToNetwork) 
} 
+1

Tốt hơn là trả lời câu hỏi của riêng bạn thay vì chỉnh sửa câu hỏi để bao gồm câu trả lời – Joakim

Trả lời

9

Đây không phải là Go cụ thể và là một tạo tác của ổ cắm TCP bên dưới hiển thị qua.

Một sơ đồ đàng hoàng trong những bước chấm dứt TCP là ở dưới cùng của trang này: http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm

Phiên bản đơn giản là khi khách hàng đóng socket của nó, nó sẽ gửi một FIN, và nhận được một ACK từ máy chủ . Sau đó nó đợi máy chủ làm như vậy. Thay vì gửi FIN mặc dù bạn đang gửi nhiều dữ liệu hơn, bị loại bỏ và ổ cắm máy khách giả định rằng bất kỳ dữ liệu nào đến từ bạn không hợp lệ, vì vậy lần sau bạn gửi cho bạn một RST. vào lỗi bạn thấy.

Quay lại chương trình của bạn, bạn cần xử lý việc này bằng cách nào đó. Nói chung, bạn có thể nghĩ đến bất kỳ ai chịu trách nhiệm bắt đầu gửi, cũng chịu trách nhiệm bắt đầu chấm dứt, do đó máy chủ của bạn nên giả định rằng nó có thể tiếp tục gửi cho đến khi đóng kết nối hoặc gặp phải lỗi. Nếu bạn cần phát hiện khách hàng một cách đáng tin cậy hơn, bạn cần phải có một số loại phản hồi ứng dụng khách trong giao thức. Bằng cách đó recv có thể được gọi trên socket và trả về 0, cảnh báo cho bạn về kết nối đã đóng.

Khi di chuyển, điều này sẽ trả về lỗi EOF từ phương thức Đọc của kết nối (hoặc từ trong Bản sao trong trường hợp của bạn).SetWriteDeadline không hoạt động bởi vì một ghi nhỏ sẽ đi và mặc dù bị mất âm thầm, hoặc khách hàng cuối cùng sẽ đáp ứng với một RST, cho bạn một lỗi.

+0

Cảm ơn bạn đã nhận xét, tôi có một ví dụ làm việc ngay bây giờ. –